diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
new file mode 100644
index 00000000..4b23e874
--- /dev/null
+++ b/CHANGELOG.adoc
@@ -0,0 +1,203 @@
+:toc: macro
+:toc-title: Table of Contents
+:toclevels: 6
+
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on https://keepachangelog.com/en/1.0.0/[Keep a Changelog],
+and this project adheres to http://sentimentalversioning.org/[Sentimental Versioning].
+
+toc::[]
+
+## 2.1.0
+
+This version have minor fixes and updates.
+
+### Added
+
+- Ability to use mixin with _POST_ requests. Example:
+
+```json
+{"@": "https://example.com[POST]"}
+```
+Would fetch the url using _POST_ instead of _GET_ (Useful for _Bubble_ or similar systems).
+
+- Ability to show ATTracking alert in iOS >= 14.5:
+Set `tracking_enabled` to true inside _settings.plist_.
+
+### Changed
+
+- Bumped to minimum iOS version `12.0`.
+
+### Fixed
+
+- Crash when passing `href` with a non object.
+- Crash in iOS 14.
+- Push notifications were using code that was non deterministic to obtain the token. Now the token is parsed correctly.
+- Random Crashes due to updating the UI in a background thread.
+- Removed deprecation warnings when using `[[UIApplication sharedApplication] openURL:]`.
+- Problems when using Tabbars.
+
+### Updated
+
+- Updated to `AFNetworking` 4.0.1 (was 3.2.1).
+- Updated to `FLEX` 4.1.1 (was 3.0.0).
+- Updated to `SocketRocket` 0.5.2 (was 0.5.1).
+- Updated to `SWTableViewCell` 0.3.8 (was 0.3.7)
+
+### People
+
+Huge thanks to the following persons that helped in this release:
+
+- Mike from https://www.fus-ed.com/ (Sponsored Firebase extension for iOS and other fixes).
+- https://github.com/vini-brito[Vinicius Brito] (Requested and sponsored the Mixin POST feature).
+- _@TT Multi_ in Telegram Group (Sponsored some Cookies and Coffee).
+- Dooble Team (Sponsored ATTracking update in iOS).
+- All the wonderful people in the https://t.me/jasonelle[Jasonelle Telegram Community].
+
+## https://github.com/jasonelle/jasonelle/releases/tag/v2.0[2.0.0]
+
+This version was released in November 2019.
+
+### Added
+
+- New Logger for Native Code. Makes easier to Spot Errors. See xcode/Jasonette/Logger/README.md[xcode/Jasonette/Logger/README.md] For more details.
+
+- Added `JasonNetworking.h` to enable configuring `AFHTTPSessionManager` and `AFJSONResponseSerializer`.
+
+- Docs on how to implement extensions.
+
+- Added http://uncrustify.sourceforge.net/[`uncrustify`] config for code style standarization.
+
+- Added new option in `href` to load a `web` with `reader mode`.
+ Based on the code by `@seletz`.
+
+```json
+{
+ "options": {
+ "reader": true
+ }
+}
+```
+
+- Added `$orientation` system event
+ that triggers when the orientation changes.
+
+- Added `$env.view.params` variable that holds the query params inside the url.
+ Example `https://example.com?param1=1¶ms2=true`. Will show `param1` and `param2` as properties inside the params dictionary. (Only for for internet addresses. _file://_ does not work.)
+
+- Added `$agent.logger` to `agent.js` that can call the system logger.
+ Methods: `$agent.logger.log`, `$agent.logger.debug`, `$agent.logger.info`, `$agent.logger.warn`, `$agent.logger.error`. As replacements of `console.log` methods for webviews.
+
+- Added optional `nonce` to url in `settings.plist`. Now is easier to invalidate server cache if needed.
+
+### Changed
+
+- Bumped to minimum iOS version `9.0`.
+
+- Improved `JasonComponentFactory.h` to take in consideration `Swift` extensions.
+
+- Establish `AppDelegate.h` as main _App Delegate_ instead of `JasonAppDelegate.h`. The later will serve as a wrapper.
+
+- Improved Code Organization.
+
+- Improved Networking Code.
+
+### Fixed
+
+- Fixed Crash on parsing local json files with wrong syntax.
+
+- Fixed Crash when no `$jason` property is present in json.
+
+- Fixed Crash when url contained html content in a json expected return.
+
+- Fixed Blank Screen when no `url` is found in `settings.plist`.
+
+- Fixed Blank Screen if you click a `Tab Item` more than once.
+
+- Fixed `WKWebView` orientation change not working. Based on the code by `@ricardojlpinto`.
+
+- Fixed Crash when using`$vision` on simulator.
+
+- Fixed not finding class when using non standard naming in extensions (now searches in lowercase too).
+
+- Fixed Crash in iOS 10 when using webcontainers. It crashed because before iOS 11 the observers to notifications does not autorelease. Solved using `INTUAutoRemoveObserver`.
+
+- Fixed Random Crash. The property `styles` in `JasonViewController` was not initialized
+ in some use cases. Now is lazy allocated
+ to prevent random crashes.
+
+- Fixed Crash when using iOS 13.
+
+### Updated
+
+- Updated to `AFNetworking` 3.2.1 (was 3.1.0).
+
+- Updated to `UICKeyChainStore` 2.1.2 (was 2.1.0).
+
+- Updated to `IQAudioRecorderController` 1.2.3 (was 1.2.0).
+
+- Updated to `SBJsonWriter` 5.0.0 (was 4.0.2).
+
+- Updated to `libPhoneNumber-iOS` 0.9.15 (was 0.8.13).
+
+- Updated to `JDStatusBarNotification` 1.6.0 (was 1.5.3).
+
+- Updated to `APAddressBook` 0.3.2 (was 0.2.3).
+
+- Updated to `MBProgressHUD` 1.1.0 (was 1.0.0).
+
+- Updated to `NSGIF` 1.2.4 (was 1.2).
+
+- Updated to `NSHash` 1.2.0 (was 1.1.0).
+
+- Updated to `DTCoreText` 1.6.23 (was 1.6.17).
+
+- Updated to `DTFoundation` 1.7.14 (was 1.7.10).
+
+- Updated to `FreeStreamer` 4.0.0 (was 3.5.7).
+
+- Updated to `JSCoreBom` 1.1.2 (was 1.1.1).
+
+- Updated to `OMGHTTPURLRQ` 3.2.4 (was 3.1.2).
+
+- Updated to `FLEX 3.0.0` (was 2.4.0).
+
+- Updated to `CYRTextView` 0.4.1 (was 0.4.0).
+
+- Updated to `HMSegmentedControl` 1.5.5 (was 1.5.2).
+
+- Updated to `INTULocationManager` 4.3.2 (was 4.2.0).
+
+### Removed
+
+- `UIWebview` Dependencies. Since Apple will stop accepting apps that use that API.
+
+### Notes
+
+- This version is a complete overhaul focusing on
+ modularization of the code and update of the libraries, improving the quality of the framework, maintaining the same json api.
+
+- The next version will be re engineered so it will be easier to maintain and find bugs. New arquitecture and possible adopting Swift Language.
+
+### People
+
+Huge thanks to the following persons that helped in this release:
+
+- https://github.com/takakeiji[Adán Miranda]: Helped with some guidance over iOS code.
+
+- `BSG`: Detected layout error in WKWebViews in iOS >= 11.
+
+- `John Mark`: Wrote a great tutorial in Bubble.is forums.
+
+- https://devschile.cl[Devs Chile]: Chilean commmunity of developers.
+
+- `Phillip`: Suggested the `nonce` addition.
+
+More people here https://jasonelle.com/docs/[https://jasonelle.com/docs/].
+
+## https://github.com/jasonelle/jasonelle/releases/tag/v1.0[1.0]
+
+First version of the _Jasonette_ Mobile Framework. This version was the same as the latest development version of https://github.com/jasonette/jasonette-ios[Ethan's Jasonette].
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index be7a0b59..00000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,152 +0,0 @@
-# Changelog
-All notable changes to this project will be documented in this file.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to [Sentimental Versioning](http://sentimentalversioning.org/).
-
-## [3.0.0] Next Release
-
-This version is the current in development. Will be released after the week of *6th November 2020*.
-
-## [2.0.0](https://github.com/jasonelle/jasonelle/releases/tag/v2.0)
-
-This version was released in November 2019.
-
-### Added
-
-- New Logger for Native Code. Makes easier to Spot Errors. See [xcode/Jasonette/Logger/README.md](xcode/Jasonette/Logger/README.md) For more details.
-
-- Added `JasonNetworking.h` to enable configuring `AFHTTPSessionManager` and `AFJSONResponseSerializer`.
-
-- Docs on how to implement extensions.
-
-- Added [`uncrustify`](http://uncrustify.sourceforge.net/) config for code style standarization.
-
-- Added new option in `href` to load a `web` with `reader mode`.
-Based on the code by `@seletz`.
-
-```json
-{
- "options": {
- "reader": true
- }
-}
-```
-
-- Added `$orientation` system event
-that triggers when the orientation changes.
-
-- Added `$env.view.params` variable that holds the query params inside the url.
-Example `https://example.com?param1=1¶ms2=true`. Will show `param1` and `param2` as properties inside the params dictionary.
-
-- Added `$agent.logger` to `agent.js` that can call the system logger.
-Methods: `$agent.logger.log`, `$agent.logger.debug`, `$agent.logger.info`, `$agent.logger.warn`, `$agent.logger.error`. As replacements of `console.log` methods for webviews.
-
-- Added optional `nonce` to url in `settings.plist`. Now is easier to invalidate server cache if needed.
-
-### Changed
-
-- Bumped to minimum iOS version `9.0`.
-
-- Improved `JasonComponentFactory.h` to take in consideration `Swift` extensions.
-
-- Establish `AppDelegate.h` as main *App Delegate* instead of `JasonAppDelegate.h`. The later will serve as a wrapper.
-
-- Improved Code Organization.
-
-- Improved Networking Code.
-
-### Fixed
-
-- Fixed Crash on parsing local json files with wrong syntax.
-
-- Fixed Crash when no `$jason` property is present in json.
-
-- Fixed Crash when url contained html content in a json expected return.
-
-- Fixed Blank Screen when no `url` is found in `settings.plist`.
-
-- Fixed Blank Screen if you click a `Tab Item` more than once.
-
-- Fixed `WKWebView` orientation change not working. Based on the code by `@ricardojlpinto`.
-
-- Fixed Crash when using`$vision` on simulator.
-
-- Fixed not finding class when using non standard naming in extensions (now searches in lowercase too).
-
-- Fixed Crash in iOS 10 when using webcontainers. It crashed because before iOS 11 the observers to notifications does not autorelease. Solved using `INTUAutoRemoveObserver`.
-
-- Fixed Random Crash. The property `styles` in `JasonViewController` was not initialized
-in some use cases. Now is lazy allocated
-to prevent random crashes.
-
-### Updated
-
-- Updated to `AFNetworking` 3.2.1 (was 3.1.0).
-
-- Updated to `UICKeyChainStore` 2.1.2 (was 2.1.0).
-
-- Updated to `IQAudioRecorderController` 1.2.3 (was 1.2.0).
-
-- Updated to `SBJsonWriter` 5.0.0 (was 4.0.2).
-
-- Updated to `libPhoneNumber-iOS` 0.9.15 (was 0.8.13).
-
-- Updated to `JDStatusBarNotification` 1.6.0 (was 1.5.3).
-
-- Updated to `APAddressBook` 0.3.2 (was 0.2.3).
-
-- Updated to `MBProgressHUD` 1.1.0 (was 1.0.0).
-
-- Updated to `NSGIF` 1.2.4 (was 1.2).
-
-- Updated to `NSHash` 1.2.0 (was 1.1.0).
-
-- Updated to `DTCoreText` 1.6.23 (was 1.6.17).
-
-- Updated to `DTFoundation` 1.7.14 (was 1.7.10).
-
-- Updated to `FreeStreamer` 4.0.0 (was 3.5.7).
-
-- Updated to `JSCoreBom` 1.1.2 (was 1.1.1).
-
-- Updated to `OMGHTTPURLRQ` 3.2.4 (was 3.1.2).
-
-- Updated to `FLEX 3.0.0` (was 2.4.0).
-
-- Updated to `CYRTextView` 0.4.1 (was 0.4.0).
-
-- Updated to `HMSegmentedControl` 1.5.5 (was 1.5.2).
-
-- Updated to `INTULocationManager` 4.3.2 (was 4.2.0).
-
-### Removed
-
-- `UIWebview` Dependencies. Since Apple will stop accepting apps that use that API.
-
-### Notes
-
-- This version is a complete overhaul focusing on
-modularization of the code and update of the libraries, improving the quality of the framework, maintaining the same json api.
-
-- The next version will be re engineered so it will be easier to maintain and find bugs. New arquitecture and possible adopting Swift Language.
-
-### People
-
-Huge thanks to the following persons that helped in this release:
-
-- [Adán Miranda](https://github.com/takakeiji): Helped with some guidance over iOS code.
-
-- `BSG`: Detected layout error in WKWebViews in iOS >= 11.
-
-- `John Mark`: Wrote a great tutorial in Bubble.is forums.
-
-- [Devs Chile](https://devschile.cl): Chilean commmunity of developers.
-
-- `Phillip`: Suggested the `nonce` addition.
-
-More people here [https://jasonelle.com/docs/#/folks](https://jasonelle.com/docs/#/folks).
-
-## [1.0](https://github.com/jasonelle/jasonelle/releases/tag/v1.0)
-
-First version of the *Jasonette* Mobile Framework.
diff --git a/README.md b/README.md
index 343d67d9..2b609fa0 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[https://www.jasonelle.com](https://www.jasonelle.com)
-Create your own native iOS app with *JSON*. Then send it over `http(s)://` or `file://`.
+Create your own native iOS app with _JSON_. Then send it over `http(s)://` or `file://`.
## Releases
@@ -14,30 +14,28 @@ Releases are made in the [Jasonelle Repo](https://github.com/jasonelle/jasonelle
- [Jasonette v2.0.0](https://github.com/jasonelle/jasonelle/releases/tag/v2.0).
-- Jasonette *v3.0.0* (In Progress).
+- Jasonette _v2.1.0_ (In Progress).
## [Contribution](CONTRIBUTING.md)
This repository uses `develop` branch to store the bleeding
edge code. More details in the [CONTRIBUTING](CONTRIBUTING.md) file.
-## [Changelog](CHANGELOG.md)
+## [Changelog](CHANGELOG.adoc)
-The [CHANGELOG](CHANGELOG.md) contains the release notes
+The [CHANGELOG](CHANGELOG.adoc) contains the release notes
of each version.
## [Documentation](https://jasonelle.com/docs)
Documentation and examples in the [documentation repository](https://github.com/jasonelle/docs).
-
## Questions and Support
Follow or join these channels for questions and support, and to keep updated on latest releases and announcements.
[Telegram Group](https://t.me/jasonelle)
-
## [License](LICENSE)
-*Jasonette* is released under the [MPL 2.0 License](https://opensource.org/licenses/MPL-2.0).
+_Jasonette_ is released under the [MPL 2.0 License](https://opensource.org/licenses/MPL-2.0).
diff --git a/tools/legacy/.codeclimate.yml b/tools/legacy/.codeclimate.yml
deleted file mode 100644
index 2ad12e67..00000000
--- a/tools/legacy/.codeclimate.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-engines:
- fixme:
- enabled: true
- tailor:
- enabled: false
-ratings:
- paths:
- - "**.swift"
-exclude_paths:
-- "/app/Pods/"
-- "**.js"
diff --git a/tools/legacy/CONTRIBUTING.md b/tools/legacy/CONTRIBUTING.md
deleted file mode 100644
index c7280417..00000000
--- a/tools/legacy/CONTRIBUTING.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# How to contribute to Jasonette
-
-## **Want to help with documentation?**
-
-If you would like to contribute to the [documentation](https://jasonette.github.io/documentation/), let's discuss on the [documentation repository](https://github.com/Jasonette/documentation/issues).
-
-## **Do you have a bug report or a feature request?**
-
-* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/Jasonette/JASONETTE-iOS/issues).
-
-* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/Jasonette/JASONETTE-iOS/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring.
-
-
-## **Did you write a patch that fixes a bug?**
-
-* Open a new GitHub pull request with the patch.
-
-* Don't fork `master` branch. **Fork `develop` branch and send a pull request to `develop`.
-
-* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
-
-## **Did you write a cool extension?**
-
-Feel free to fork the project and [write your own extension](https://jasonette.github.io/documentation/advanced)
-
-If you wrote a cool extension, please share it with the community in the [slack channel](https://jasonette.now.sh).
-
-## **Do you have other types of questions?**
-
-* Ask any question about how to use Jasonette on the [Jasonette Slack channel](https://jasonette.now.sh).
-
-## **Project Structure**
-
-### Class hierarchy
-![hierarchy](https://raw.githubusercontent.com/gliechtenstein/images/master/hierarchy.png)
-
-Here's a brief walkthrough of how the project is structured:
-
- - **Launcher**: You can ignore this, just some files that launches the app.
- - **Config**: Normally these are the only files you will ever need to touch.
- - `Info.plist`: App setting. Normally don't need to touch this unless you're manually setting up stuff.
- - `settings.plist`: **This is the only file you will ever need to change.** Set the `url` attribute to embed that URL into the app.
- - **Core**: Core logic that handles command processing (via stack, memory, etc.), view construction, templating, and some native system actions.
- - `Jason`: The brain of Jasonette. Everything revolves around this class. Makes use of JasonStack and JasonMemory for remembering and executing actions.
- - `JasonStack`: Stack for remembering instructions (actions).
- - `JasonMemory`: Used to store actions to be executed, through stack (JasonStack) and register.
- - `JasonParser`: Parser module that calls the `Core/Lib/parser.js` file for parsing json templates.
- - `RussianDollView`: A JasonViewController protocol, you can ignore this.
- - **Assets**: You can ignore this, just some images and audio clips used by the app
- - **Lib**: Includes Javascript libraries used to execute JSON native actions.
- - `parser.js`: The main JSON parser that takes a JSON template expression and generates a final static JSON using the current register value
- - `csv.js`: CSV parser
- - `rss.js`: RSS parser
- - **Action**: Where all [actions](https://jasonette.github.io/documentation/actions/) are implemented. The implementation follows [the convention described here](https://jasonette.github.io/documentation/advanced/#2-extend-actions).
- - To build your own action extension, you can create your own custom group here and implement your own classes.
- - **View**: All view related classes.
- - `JasonViewController`: The main JSON-powered view controller. Everything view-related revolves around this class.
- - **Layer**: Implements [layers](https://jasonette.github.io/documentation/document/#bodylayers)
- - **Section**: Implements [sections](https://jasonette.github.io/documentation/document/#bodysections)
- - **Layout**: Implements [vertical and horizontal layouts](https://jasonette.github.io/documentation/layout/) that can be used inside [sections](https://jasonette.github.io/documentation/document/#bodysections)
- - **Component**: Implements [components](https://jasonette.github.io/documentation/components/), following [the convention described here](https://jasonette.github.io/documentation/advanced/#1-extend-ui-components).
- - To build your own component extension, just create your own group here and write your classes.
- - **Helper**
- - Various helper class methods used across various classes.
-
-### What files you will be touching
-
-####User
-In most cases, the only thing you will ever need to touch is the `Config/settings.plist` file. This is where you set the main url your app will launch from.
- - But even this can be automatically done using the [Setup command](https://jasonette.github.io/documentation/#step-2-setup), which means **you will never need to touch anything inside XCode** to build an app.
-
-####Advanced
-Sometimes you may want to write an [extension](https://jasonette.github.io/documentation/advanced/#extension). In this case you may need to deal with:
- - `Action`: To write action extension
- - `View/Component`: To write UI component extension
-
-####Guru
-If you find a bug **anywhere in the code**, or have any improvements anywhere else, please feel free to:
- 1. Fork the `develop` branch
- 2. Create a feature branch
- 3. Fix
- 4. Send a pull request
diff --git a/tools/legacy/LICENSE b/tools/legacy/LICENSE
deleted file mode 100644
index e85291a7..00000000
--- a/tools/legacy/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2016 gliechtenstein
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/tools/legacy/README.md b/tools/legacy/README.md
deleted file mode 100644
index 3f735707..00000000
--- a/tools/legacy/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Legacy Code
-
-This is the legacy code directory.
-It will be merged gradually with the new code base.
\ No newline at end of file
diff --git a/tools/legacy/Setup b/tools/legacy/Setup
deleted file mode 100755
index 16e93f05..00000000
--- a/tools/legacy/Setup
+++ /dev/null
@@ -1,409 +0,0 @@
-#!/bin/sh
-DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-cd "${DIR}"
-
-
-bundleidentifier(){
- formatted=(`echo $1 | sed 's/http[s]*:\/\///g' | sed 's/www\.//g' | sed 's/\.json$//g' | sed 's/\//./g' | sed 's/[^a-zA-Z0-9.-]/./g' | sed 's/\.\.+/./g'`)
- OLDIFS=$IFS
- IFS=.
- set -f
- array=($formatted)
- str=""
- IFS=$OLDIFS
- for (( i=${#array[@]}-1 ; i>=0 ; i-- )) ; do
- str+="${array[i]}"
- str+="."
- done
- new_bundle_id=(`echo $str | sed 's/\.$//g'`)
- sed -i '' "s/PRODUCT_BUNDLE_IDENTIFIER[^;]*;/PRODUCT_BUNDLE_IDENTIFIER\ =\ $new_bundle_id;/g" app/Jasonette.xcodeproj/project.pbxproj
- set +f
-}
-
-settingsplist(){
-cat > app/Jasonette/settings.plist <
-
-
-
- client_id
-
- client_secret
-
- url
- $1
- loading
-
- launch
- file://preload.json
- debug
-
-
-
-EOF
-}
-
-infoplist(){
-# same as bundleidentifier()
-
-cat > app/Jasonette/Info.plist <
-
-
-
- NSPhotoLibraryUsageDescription
- used to access photo library
- UIBackgroundModes
-
- audio
-
- NSMicrophoneUsageDescription
- used to access microphone api
- NSAppleMusicUsageDescription
- used to access media library
- NSContactsUsageDescription
- used to access contacts api
- NSCameraUsageDescription
- used to access camera api
- NSLocationWhenInUseUsageDescription
- used to access location api
- CFBundleDevelopmentRegion
- en
- CFBundleExecutable
- \$(EXECUTABLE_NAME)
- CFBundleIdentifier
- \$(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $1
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- 1.0
- CFBundleSignature
- ????
- CFBundleURLTypes
-
-
- CFBundleURLName
-
- CFBundleURLSchemes
-
- $3
-
-
-
- CFBundleVersion
- 1
- LSRequiresIPhoneOS
-
- NSAppTransportSecurity
-
- NSAllowsArbitraryLoads
-
-
- UILaunchStoryboardName
- LaunchScreen
- UIMainStoryboardFile
-
- UIRequiredDeviceCapabilities
-
- armv7
-
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UISupportedInterfaceOrientations~ipad
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
-
-
-EOF
-}
-
-generate_iconset(){
- local full_icon="$1"
-
- local type=`file -b ${full_icon}`
- if [[ $type =~ JPEG ]]
- then
- cp "$full_icon" Icon-20.jpg &> /dev/null
- sips -z 29 29 Icon-20.jpg &> /dev/null
- sips -s format png Icon-20.jpg --out Icon-20.png &> /dev/null
-
- cp "$full_icon" Icon-Small.jpg &> /dev/null
- sips -z 29 29 Icon-Small.jpg &> /dev/null
- sips -s format png Icon-Small.jpg --out Icon-Small.png &> /dev/null
-
- cp "$full_icon" Icon-Small@2x.jpg &> /dev/null
- sips -z 58 58 Icon-Small@2x.jpg &> /dev/null
- sips -s format png Icon-Small@2x.jpg --out Icon-Small@2x.png &> /dev/null
-
- cp "$full_icon" Icon-Small@3x.jpg &> /dev/null
- sips -z 87 87 Icon-Small@3x.jpg &> /dev/null
- sips -s format png Icon-Small@3x.jpg --out Icon-Small@3x.png &> /dev/null
-
- cp "$full_icon" Icon-Small-50.jpg &> /dev/null
- sips -z 50 50 Icon-Small-50.jpg &> /dev/null
- sips -s format png Icon-Small-50.jpg --out Icon-Small-50.png &> /dev/null
-
- cp "$full_icon" Icon-Small-50@2x.jpg &> /dev/null
- sips -z 100 100 Icon-Small-50@2x.jpg &> /dev/null
- sips -s format png Icon-Small-50@2x.jpg --out Icon-Small-50@2x.png &> /dev/null
-
- cp "$full_icon" Icon.jpg &> /dev/null
- sips -z 57 57 Icon.jpg &> /dev/null
- sips -s format png Icon.jpg --out Icon.png &> /dev/null
-
- cp "$full_icon" Icon@2x.jpg &> /dev/null
- sips -z 114 114 Icon@2x.jpg &> /dev/null
- sips -s format png Icon@2x.jpg --out Icon@2x.png &> /dev/null
-
- cp "$full_icon" Icon-20@2x.jpg &> /dev/null
- sips -z 40 40 Icon-20@2x.jpg &> /dev/null
- sips -s format png Icon-20@2x.jpg --out Icon-20@2x.png &> /dev/null
-
- cp "$full_icon" Icon-40.jpg &> /dev/null
- sips -z 40 40 Icon-40.jpg &> /dev/null
- sips -s format png Icon-40.jpg --out Icon-40.png &> /dev/null
-
- cp "$full_icon" Icon-20@3x.jpg &> /dev/null
- sips -z 60 60 Icon-20@3x.jpg &> /dev/null
- sips -s format png Icon-20@3x.jpg --out Icon-20@3x.png &> /dev/null
-
- cp "$full_icon" Icon-40@2x.jpg &> /dev/null
- sips -z 80 80 Icon-40@2x.jpg &> /dev/null
- sips -s format png Icon-40@2x.jpg --out Icon-40@2x.png &> /dev/null
-
- cp "$full_icon" Icon-40@3x.jpg &> /dev/null
- sips -z 120 120 Icon-40@3x.jpg &> /dev/null
- sips -s format png Icon-40@3x.jpg --out Icon-40@3x.png &> /dev/null
-
- cp "$full_icon" Icon-60@2x.jpg &> /dev/null
- sips -z 120 120 Icon-60@2x.jpg &> /dev/null
- sips -s format png Icon-60@2x.jpg --out Icon-60@2x.png &> /dev/null
-
- cp "$full_icon" Icon-60@3x.jpg &> /dev/null
- sips -z 180 180 Icon-60@3x.jpg &> /dev/null
- sips -s format png Icon-60@3x.jpg --out Icon-60@3x.png &> /dev/null
-
- cp "$full_icon" Icon-72.jpg &> /dev/null
- sips -z 72 72 Icon-72.jpg &> /dev/null
- sips -s format png Icon-72.jpg --out Icon-72.png &> /dev/null
-
- cp "$full_icon" Icon-72@2x.jpg &> /dev/null
- sips -z 144 144 Icon-72@2x.jpg &> /dev/null
- sips -s format png Icon-72@2x.jpg --out Icon-72@2x.png &> /dev/null
-
- cp "$full_icon" Icon-76.jpg &> /dev/null
- sips -z 76 76 Icon-76.jpg &> /dev/null
- sips -s format png Icon-76.jpg --out Icon-76.png &> /dev/null
-
- cp "$full_icon" Icon-76@2x.jpg &> /dev/null
- sips -z 152 152 Icon-76@2x.jpg &> /dev/null
- sips -s format png Icon-76@2x.jpg --out Icon-76@2x.png &> /dev/null
-
- cp "$full_icon" Icon-83.5@2x.jpg &> /dev/null
- sips -z 167 167 Icon-83.5@2x.jpg &> /dev/null
- sips -s format png Icon-83.5@2x.jpg --out Icon-83.5@2x.png &> /dev/null
-
- cp "$full_icon" Icon-1024.jpg &> /dev/null
- sips -z 1024 1024 Icon-1024.jpg &> /dev/null
- sips -s format png Icon-1024.jpg --out Icon-1024.png &> /dev/null
-
- rm Icon-20.jpg
- rm Icon-20@2x.jpg
- rm Icon-20@3x.jpg
- rm Icon-Small.jpg
- rm Icon-Small@2x.jpg
- rm Icon-Small@3x.jpg
- rm Icon-Small-50.jpg
- rm Icon-Small-50@2x.jpg
- rm Icon.jpg
- rm Icon@2x.jpg
- rm Icon-40.jpg
- rm Icon-40@2x.jpg
- rm Icon-40@3x.jpg
- rm Icon-60@2x.jpg
- rm Icon-60@3x.jpg
- rm Icon-72.jpg
- rm Icon-72@2x.jpg
- rm Icon-76.jpg
- rm Icon-76@2x.jpg
- rm Icon-83.5@2x.jpg
- rm Icon-1024.jpg
-
- else
- cp "$full_icon" Icon-20.png &> /dev/null
- sips -z 20 20 Icon-20.png &> /dev/null
-
- cp "$full_icon" Icon-Small.png &> /dev/null
- sips -z 29 29 Icon-Small.png &> /dev/null
-
- cp "$full_icon" Icon-20@2x.png &> /dev/null
- sips -z 40 40 Icon-20@2x.png &> /dev/null
-
- cp "$full_icon" Icon-Small@2x.png &> /dev/null
- sips -z 58 58 Icon-Small@2x.png &> /dev/null
-
- cp "$full_icon" Icon-20@3x.png &> /dev/null
- sips -z 60 60 Icon-20@3x.png &> /dev/null
-
- cp "$full_icon" Icon-Small@3x.png &> /dev/null
- sips -z 87 87 Icon-Small@3x.png &> /dev/null
-
- cp "$full_icon" Icon-Small-50.png &> /dev/null
- sips -z 50 50 Icon-Small-50.png &> /dev/null
-
- cp "$full_icon" Icon-Small-50@2x.png &> /dev/null
- sips -z 100 100 Icon-Small-50@2x.png &> /dev/null
-
- cp "$full_icon" Icon.png &> /dev/null
- sips -z 57 57 Icon.png &> /dev/null
-
- cp "$full_icon" Icon@2x.png &> /dev/null
- sips -z 114 114 Icon@2x.png &> /dev/null
-
- cp "$full_icon" Icon-40.png &> /dev/null
- sips -z 40 40 Icon-40.png &> /dev/null
-
- cp "$full_icon" Icon-40@2x.png &> /dev/null
- sips -z 80 80 Icon-40@2x.png &> /dev/null
-
- cp "$full_icon" Icon-40@3x.png &> /dev/null
- sips -z 120 120 Icon-40@3x.png &> /dev/null
-
- cp "$full_icon" Icon-60@2x.png &> /dev/null
- sips -z 120 120 Icon-60@2x.png &> /dev/null
-
- cp "$full_icon" Icon-60@3x.png &> /dev/null
- sips -z 180 180 Icon-60@3x.png &> /dev/null
-
- cp "$full_icon" Icon-72.png &> /dev/null
- sips -z 72 72 Icon-72.png &> /dev/null
-
- cp "$full_icon" Icon-72@2x.png &> /dev/null
- sips -z 144 144 Icon-72@2x.png &> /dev/null
-
- cp "$full_icon" Icon-76.png &> /dev/null
- sips -z 76 76 Icon-76.png &> /dev/null
-
- cp "$full_icon" Icon-76@2x.png &> /dev/null
- sips -z 152 152 Icon-76@2x.png &> /dev/null
-
- cp "$full_icon" Icon-83.5@2x.png &> /dev/null
- sips -z 167 167 Icon-83.5@2x.png &> /dev/null
-
- cp "$full_icon" Icon-1024.png &> /dev/null
- sips -z 1024 1024 Icon-1024.png &> /dev/null
- fi
-
- mv Icon-20.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-20.png &> /dev/null
- mv Icon-20@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-20@2x.png &> /dev/null
- mv Icon-20@3x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-20@3x.png &> /dev/null
- mv Icon-Small.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small.png &> /dev/null
- mv Icon-Small@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small@2x.png &> /dev/null
- mv Icon-Small@3x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small@3x.png &> /dev/null
- mv Icon-Small-50.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small-50.png &> /dev/null
- mv Icon-Small-50@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png &> /dev/null
- mv Icon.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon.png &> /dev/null
- mv Icon@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon@2x.png &> /dev/null
- mv Icon-40.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-40.png &> /dev/null
- mv Icon-40@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-40@2x.png &> /dev/null
- mv Icon-40@3x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-40@3x.png &> /dev/null
- mv Icon-60@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-60@2x.png &> /dev/null
- mv Icon-60@3x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-60@3x.png &> /dev/null
- mv Icon-72.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-72.png &> /dev/null
- mv Icon-72@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-72@2x.png &> /dev/null
- mv Icon-76.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-76.png &> /dev/null
- mv Icon-76@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-76@2x.png &> /dev/null
- mv Icon-83.5@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-83.5@2x.png &> /dev/null
- mv Icon-1024.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-1024.png &> /dev/null
-
-}
-
-clear
-RED='\033[0;31m'
-GREEN='\033[1;32m'
-YELLOW='\033[1;33m'
-LIGHTBLUE='\033[1;94m'
-NONE='\033[00m'
-
-echo "${RED}WELCOME TO JASONETTE!"
-echo ""
-echo "${GREEN}1. Try the demo app (Recommended if you're new)"
-echo "2. Create a new app"
-echo "3. Continue working on the existing app${NONE}"
-echo ""
-
-read -p "Select 1, 2, or 3 and press enter: (1) " option
-
-if ((option == 2))
-then
- read -p "[NAME] App name: " name
-
- read -p "[NAME] HOME JSON URL if you have one: (or press Enter to create one): " url
-
- url_scheme=(`echo -n $url | openssl sha1`)
- url_scheme="J${url_scheme}"
-
- if [ ${#url} -gt 0 ]
- then
- settingsplist "$url"
- infoplist "$name" "$url" "$url_scheme"
- bundleidentifier "$url"
- else
- echo ""
- echo "${LIGHTBLUE}Opening Jasonbase.com .."
- echo "Jasonbase is a free JSON hosting site."
- echo "Try writing one, save it, and come back with a URL, I'll wait.${NONE}"
- echo ""
- open https://www.jasonbase.com
- read -p "[URL] Enter the JSON URL: " url
- while [ ${#url} -eq 0 ]
- do
- read -p "[URL] Enter the JSON URL: " url
- done
- settingsplist "$url"
- infoplist "$name" "$url" "$url_scheme"
- bundleidentifier "$url"
- fi
-
- read -p "[ICON] Add a PNG or JPG image to the 'icon' folder and press Enter.." -r e
- for file in icon/*.{jpg,jpeg,JPG,JPEG,png,PNG}; do
- [ -e "$file" ] && generate_iconset "$file" && break
- done
-
- echo "${GREEN}* URL Scheme: ${url_scheme}://"
- echo "* OAuth redirect uri: ${url_scheme}://oauth${GREEN}"
-
- echo "Opening XCode workspace. Please wait..."
- open app/Jasonette.xcworkspace
- sleep 10
-elif ((option == 3))
-then
- echo "Opening XCode workspace. Please wait..."
- open app/Jasonette.xcworkspace
- sleep 10
-else
- new_bundle_id=`uuidgen`
- settingsplist https://jasonette.github.io/Jasonpedia/hello.json
- bundleidentifier "com.jasonette.seed.${new_bundle_id}"
- echo ""
- echo "You can view the JSON for the included demo app at:"
- echo "https://github.com/Jasonette/Jasonpedia"
- echo ""
- echo "Opening XCode workspace. Please wait..."
- open app/Jasonette.xcworkspace
- echo ""
- echo ""
- sleep 10
-fi
diff --git a/tools/legacy/refresh-icon b/tools/legacy/refresh-icon
deleted file mode 100755
index ec62205c..00000000
--- a/tools/legacy/refresh-icon
+++ /dev/null
@@ -1,208 +0,0 @@
-#!/bin/sh
-DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-cd "${DIR}"
-
-
-generate_iconset(){
- local full_icon="$1"
-
- local type=`file -b ${full_icon}`
- if [[ $type =~ JPEG ]]
- then
- cp "$full_icon" Icon-20.jpg &> /dev/null
- sips -z 29 29 Icon-20.jpg &> /dev/null
- sips -s format png Icon-20.jpg --out Icon-20.png &> /dev/null
-
- cp "$full_icon" Icon-Small.jpg &> /dev/null
- sips -z 29 29 Icon-Small.jpg &> /dev/null
- sips -s format png Icon-Small.jpg --out Icon-Small.png &> /dev/null
-
- cp "$full_icon" Icon-Small@2x.jpg &> /dev/null
- sips -z 58 58 Icon-Small@2x.jpg &> /dev/null
- sips -s format png Icon-Small@2x.jpg --out Icon-Small@2x.png &> /dev/null
-
- cp "$full_icon" Icon-Small@3x.jpg &> /dev/null
- sips -z 87 87 Icon-Small@3x.jpg &> /dev/null
- sips -s format png Icon-Small@3x.jpg --out Icon-Small@3x.png &> /dev/null
-
- cp "$full_icon" Icon-Small-50.jpg &> /dev/null
- sips -z 50 50 Icon-Small-50.jpg &> /dev/null
- sips -s format png Icon-Small-50.jpg --out Icon-Small-50.png &> /dev/null
-
- cp "$full_icon" Icon-Small-50@2x.jpg &> /dev/null
- sips -z 100 100 Icon-Small-50@2x.jpg &> /dev/null
- sips -s format png Icon-Small-50@2x.jpg --out Icon-Small-50@2x.png &> /dev/null
-
- cp "$full_icon" Icon.jpg &> /dev/null
- sips -z 57 57 Icon.jpg &> /dev/null
- sips -s format png Icon.jpg --out Icon.png &> /dev/null
-
- cp "$full_icon" Icon@2x.jpg &> /dev/null
- sips -z 114 114 Icon@2x.jpg &> /dev/null
- sips -s format png Icon@2x.jpg --out Icon@2x.png &> /dev/null
-
- cp "$full_icon" Icon-20@2x.jpg &> /dev/null
- sips -z 40 40 Icon-20@2x.jpg &> /dev/null
- sips -s format png Icon-20@2x.jpg --out Icon-20@2x.png &> /dev/null
-
- cp "$full_icon" Icon-40.jpg &> /dev/null
- sips -z 40 40 Icon-40.jpg &> /dev/null
- sips -s format png Icon-40.jpg --out Icon-40.png &> /dev/null
-
- cp "$full_icon" Icon-20@3x.jpg &> /dev/null
- sips -z 60 60 Icon-20@3x.jpg &> /dev/null
- sips -s format png Icon-20@3x.jpg --out Icon-20@3x.png &> /dev/null
-
- cp "$full_icon" Icon-40@2x.jpg &> /dev/null
- sips -z 80 80 Icon-40@2x.jpg &> /dev/null
- sips -s format png Icon-40@2x.jpg --out Icon-40@2x.png &> /dev/null
-
- cp "$full_icon" Icon-40@3x.jpg &> /dev/null
- sips -z 120 120 Icon-40@3x.jpg &> /dev/null
- sips -s format png Icon-40@3x.jpg --out Icon-40@3x.png &> /dev/null
-
- cp "$full_icon" Icon-60@2x.jpg &> /dev/null
- sips -z 120 120 Icon-60@2x.jpg &> /dev/null
- sips -s format png Icon-60@2x.jpg --out Icon-60@2x.png &> /dev/null
-
- cp "$full_icon" Icon-60@3x.jpg &> /dev/null
- sips -z 180 180 Icon-60@3x.jpg &> /dev/null
- sips -s format png Icon-60@3x.jpg --out Icon-60@3x.png &> /dev/null
-
- cp "$full_icon" Icon-72.jpg &> /dev/null
- sips -z 72 72 Icon-72.jpg &> /dev/null
- sips -s format png Icon-72.jpg --out Icon-72.png &> /dev/null
-
- cp "$full_icon" Icon-72@2x.jpg &> /dev/null
- sips -z 144 144 Icon-72@2x.jpg &> /dev/null
- sips -s format png Icon-72@2x.jpg --out Icon-72@2x.png &> /dev/null
-
- cp "$full_icon" Icon-76.jpg &> /dev/null
- sips -z 76 76 Icon-76.jpg &> /dev/null
- sips -s format png Icon-76.jpg --out Icon-76.png &> /dev/null
-
- cp "$full_icon" Icon-76@2x.jpg &> /dev/null
- sips -z 152 152 Icon-76@2x.jpg &> /dev/null
- sips -s format png Icon-76@2x.jpg --out Icon-76@2x.png &> /dev/null
-
- cp "$full_icon" Icon-83.5@2x.jpg &> /dev/null
- sips -z 167 167 Icon-83.5@2x.jpg &> /dev/null
- sips -s format png Icon-83.5@2x.jpg --out Icon-83.5@2x.png &> /dev/null
-
- cp "$full_icon" Icon-1024@1x.jpg &> /dev/null
- sips -z 1024 1024 Icon-1024@1x.jpg &> /dev/null
- sips -s format png Icon-1024@1x.jpg --out Icon-1024@1x.png &> /dev/null
-
- rm Icon-20.jpg
- rm Icon-20@2x.jpg
- rm Icon-20@3x.jpg
- rm Icon-Small.jpg
- rm Icon-Small@2x.jpg
- rm Icon-Small@3x.jpg
- rm Icon-Small-50.jpg
- rm Icon-Small-50@2x.jpg
- rm Icon.jpg
- rm Icon@2x.jpg
- rm Icon-40.jpg
- rm Icon-40@2x.jpg
- rm Icon-40@3x.jpg
- rm Icon-60@2x.jpg
- rm Icon-60@3x.jpg
- rm Icon-72.jpg
- rm Icon-72@2x.jpg
- rm Icon-76.jpg
- rm Icon-76@2x.jpg
- rm Icon-83.5@2x.jpg
- rm Icon-1024@1x.jpg
-
- else
- cp "$full_icon" Icon-20.png &> /dev/null
- sips -z 20 20 Icon-20.png &> /dev/null
-
- cp "$full_icon" Icon-Small.png &> /dev/null
- sips -z 29 29 Icon-Small.png &> /dev/null
-
- cp "$full_icon" Icon-20@2x.png &> /dev/null
- sips -z 40 40 Icon-20@2x.png &> /dev/null
-
- cp "$full_icon" Icon-Small@2x.png &> /dev/null
- sips -z 58 58 Icon-Small@2x.png &> /dev/null
-
- cp "$full_icon" Icon-20@3x.png &> /dev/null
- sips -z 60 60 Icon-20@3x.png &> /dev/null
-
- cp "$full_icon" Icon-Small@3x.png &> /dev/null
- sips -z 87 87 Icon-Small@3x.png &> /dev/null
-
- cp "$full_icon" Icon-Small-50.png &> /dev/null
- sips -z 50 50 Icon-Small-50.png &> /dev/null
-
- cp "$full_icon" Icon-Small-50@2x.png &> /dev/null
- sips -z 100 100 Icon-Small-50@2x.png &> /dev/null
-
- cp "$full_icon" Icon.png &> /dev/null
- sips -z 57 57 Icon.png &> /dev/null
-
- cp "$full_icon" Icon@2x.png &> /dev/null
- sips -z 114 114 Icon@2x.png &> /dev/null
-
- cp "$full_icon" Icon-40.png &> /dev/null
- sips -z 40 40 Icon-40.png &> /dev/null
-
- cp "$full_icon" Icon-40@2x.png &> /dev/null
- sips -z 80 80 Icon-40@2x.png &> /dev/null
-
- cp "$full_icon" Icon-40@3x.png &> /dev/null
- sips -z 120 120 Icon-40@3x.png &> /dev/null
-
- cp "$full_icon" Icon-60@2x.png &> /dev/null
- sips -z 120 120 Icon-60@2x.png &> /dev/null
-
- cp "$full_icon" Icon-60@3x.png &> /dev/null
- sips -z 180 180 Icon-60@3x.png &> /dev/null
-
- cp "$full_icon" Icon-72.png &> /dev/null
- sips -z 72 72 Icon-72.png &> /dev/null
-
- cp "$full_icon" Icon-72@2x.png &> /dev/null
- sips -z 144 144 Icon-72@2x.png &> /dev/null
-
- cp "$full_icon" Icon-76.png &> /dev/null
- sips -z 76 76 Icon-76.png &> /dev/null
-
- cp "$full_icon" Icon-76@2x.png &> /dev/null
- sips -z 152 152 Icon-76@2x.png &> /dev/null
-
- cp "$full_icon" Icon-83.5@2x.png &> /dev/null
- sips -z 167 167 Icon-83.5@2x.png &> /dev/null
-
- cp "$full_icon" Icon-1024@1x.png &> /dev/null
- sips -z 1024 1024 Icon-1024@1x.png &> /dev/null
- fi
-
- mv Icon-20.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-20.png &> /dev/null
- mv Icon-20@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-20@2x.png &> /dev/null
- mv Icon-20@3x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-20@3x.png &> /dev/null
- mv Icon-Small.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small.png &> /dev/null
- mv Icon-Small@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small@2x.png &> /dev/null
- mv Icon-Small@3x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small@3x.png &> /dev/null
- mv Icon-Small-50.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small-50.png &> /dev/null
- mv Icon-Small-50@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png &> /dev/null
- mv Icon.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon.png &> /dev/null
- mv Icon@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon@2x.png &> /dev/null
- mv Icon-40.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-40.png &> /dev/null
- mv Icon-40@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-40@2x.png &> /dev/null
- mv Icon-40@3x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-40@3x.png &> /dev/null
- mv Icon-60@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-60@2x.png &> /dev/null
- mv Icon-60@3x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-60@3x.png &> /dev/null
- mv Icon-72.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-72.png &> /dev/null
- mv Icon-72@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-72@2x.png &> /dev/null
- mv Icon-76.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-76.png &> /dev/null
- mv Icon-76@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-76@2x.png &> /dev/null
- mv Icon-83.5@2x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-83.5@2x.png &> /dev/null
- mv Icon-1024@1x.png app/Jasonette/Media.xcassets/AppIcon.appiconset/Icon-1024@1x.png &> /dev/null
-}
-
-for file in icon/*.{jpg,jpeg,JPG,JPEG,png,PNG}; do
- [ -e "$file" ] && generate_iconset "$file" && break
-done
diff --git a/tools/uncrustify/uncrustify.sh b/tools/uncrustify/uncrustify.sh
index 8f0a6c5e..cc112174 100755
--- a/tools/uncrustify/uncrustify.sh
+++ b/tools/uncrustify/uncrustify.sh
@@ -1,5 +1,8 @@
#!/usr/bin/env sh
# Execute this command in the root directory
+# needs uncrustify to be installed
+# try:
+# $ brew install uncrustify
find ./xcode/Jasonette -name "*.h" | xargs uncrustify -c ./tools/uncrustify/uncrustify.cfg --replace --no-backup
find ./xcode/Jasonette -name "*.m" | xargs uncrustify -c ./tools/uncrustify/uncrustify.cfg --replace --no-backup
\ No newline at end of file
diff --git a/xcode/App/custom.js b/xcode/App/custom.js
new file mode 100644
index 00000000..6ec7083b
--- /dev/null
+++ b/xcode/App/custom.js
@@ -0,0 +1 @@
+// This file is appended on each request for $webcontainer.
diff --git a/xcode/App/settings.plist b/xcode/App/settings.plist
index b1c17c96..cfe64dcf 100644
--- a/xcode/App/settings.plist
+++ b/xcode/App/settings.plist
@@ -4,6 +4,8 @@
url
file://hello.json
+ tracking_enabled
+
append_nonce_to_url
client_id
diff --git a/xcode/Jasonette.xcodeproj/project.pbxproj b/xcode/Jasonette.xcodeproj/project.pbxproj
index 33636980..d17849b0 100644
--- a/xcode/Jasonette.xcodeproj/project.pbxproj
+++ b/xcode/Jasonette.xcodeproj/project.pbxproj
@@ -96,6 +96,8 @@
EF07A20622D6186100D0D3FF /* JasonAgentService.m in Sources */ = {isa = PBXBuildFile; fileRef = EF07A1B522D6186100D0D3FF /* JasonAgentService.m */; };
EF0D48762366307500CCC7AF /* hello.test.hjson in Resources */ = {isa = PBXBuildFile; fileRef = EF0D48752366307500CCC7AF /* hello.test.hjson */; };
EF0D487A2366626500CCC7AF /* hjson.js in Resources */ = {isa = PBXBuildFile; fileRef = EF0D48792366626500CCC7AF /* hjson.js */; };
+ EF342844267C382D00AE3920 /* JasonATTrackingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EF342843267C382D00AE3920 /* JasonATTrackingManager.m */; };
+ EF4DEF39243D62140060AE4E /* custom.js in Resources */ = {isa = PBXBuildFile; fileRef = EF4DEF38243D62140060AE4E /* custom.js */; };
EF72736823465463005C5515 /* INTUAutoRemoveObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = EF72736623465463005C5515 /* INTUAutoRemoveObserver.m */; };
EF82979B22D30A71003ED64B /* jr.sample.json in Resources */ = {isa = PBXBuildFile; fileRef = EF82979922D30A71003ED64B /* jr.sample.json */; };
EF9781C322D9A0810024FDAE /* hello.json in Resources */ = {isa = PBXBuildFile; fileRef = EF9781C222D9A0810024FDAE /* hello.json */; };
@@ -257,6 +259,9 @@
EF07A1B622D6186100D0D3FF /* JasonAgentService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JasonAgentService.h; sourceTree = ""; };
EF0D48752366307500CCC7AF /* hello.test.hjson */ = {isa = PBXFileReference; lastKnownFileType = text; path = hello.test.hjson; sourceTree = ""; };
EF0D48792366626500CCC7AF /* hjson.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = hjson.js; sourceTree = ""; };
+ EF342842267C382D00AE3920 /* JasonATTrackingManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JasonATTrackingManager.h; sourceTree = ""; };
+ EF342843267C382D00AE3920 /* JasonATTrackingManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JasonATTrackingManager.m; sourceTree = ""; };
+ EF4DEF38243D62140060AE4E /* custom.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = custom.js; sourceTree = ""; };
EF72736623465463005C5515 /* INTUAutoRemoveObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = INTUAutoRemoveObserver.m; sourceTree = ""; };
EF72736723465463005C5515 /* INTUAutoRemoveObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INTUAutoRemoveObserver.h; sourceTree = ""; };
EF82979222D28D96003ED64B /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Jasonette/Info.plist; sourceTree = SOURCE_ROOT; };
@@ -336,6 +341,7 @@
EF072F5E22D28B2400973DE1 /* App */ = {
isa = PBXGroup;
children = (
+ EF4DEF38243D62140060AE4E /* custom.js */,
EF072F5F22D28B2400973DE1 /* settings.plist */,
EF9781C222D9A0810024FDAE /* hello.json */,
);
@@ -513,6 +519,7 @@
children = (
EF07A10C22D6186100D0D3FF /* JasonAppDelegate.h */,
EF07A10D22D6186100D0D3FF /* JasonAppDelegate.m */,
+ EF342846267C383500AE3920 /* ATTrackingManager */,
);
path = AppDelegate;
sourceTree = "";
@@ -1066,6 +1073,15 @@
path = hjson;
sourceTree = "";
};
+ EF342846267C383500AE3920 /* ATTrackingManager */ = {
+ isa = PBXGroup;
+ children = (
+ EF342842267C382D00AE3920 /* JasonATTrackingManager.h */,
+ EF342843267C382D00AE3920 /* JasonATTrackingManager.m */,
+ );
+ path = ATTrackingManager;
+ sourceTree = "";
+ };
EF72736523465463005C5515 /* INTUAutoRemoveObserver */ = {
isa = PBXGroup;
children = (
@@ -1111,7 +1127,7 @@
5E3176511D1E400C00D87778 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1010;
+ LastUpgradeCheck = 1240;
ORGANIZATIONNAME = Jasonette;
TargetAttributes = {
5E3176581D1E400C00D87778 = {
@@ -1132,7 +1148,7 @@
};
buildConfigurationList = 5E3176541D1E400C00D87778 /* Build configuration list for PBXProject "Jasonette" */;
compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
+ developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@@ -1168,6 +1184,7 @@
EF07303D22D28B2400973DE1 /* LaunchScreen.storyboard in Resources */,
EF07303C22D28B2400973DE1 /* Media.xcassets in Resources */,
EF07A1D122D6186100D0D3FF /* rss.js in Resources */,
+ EF4DEF39243D62140060AE4E /* custom.js in Resources */,
EF0D48762366307500CCC7AF /* hello.test.hjson in Resources */,
EF07A1CC22D6186100D0D3FF /* Next@2x.png in Resources */,
EF0D487A2366626500CCC7AF /* hjson.js in Resources */,
@@ -1211,10 +1228,8 @@
buildActionMask = 2147483647;
files = (
);
- inputFileListPaths = (
- );
inputPaths = (
- "${SRCROOT}/Pods/Target Support Files/Pods-Jasonette/Pods-Jasonette-frameworks.sh",
+ "${PODS_ROOT}/Target Support Files/Pods-Jasonette/Pods-Jasonette-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework",
"${BUILT_PRODUCTS_DIR}/AFOAuth2Manager/AFOAuth2Manager.framework",
"${BUILT_PRODUCTS_DIR}/AHKActionSheet/AHKActionSheet.framework",
@@ -1255,8 +1270,6 @@
"${BUILT_PRODUCTS_DIR}/libPhoneNumber-iOS/libPhoneNumber_iOS.framework",
);
name = "[CP] Embed Pods Frameworks";
- outputFileListPaths = (
- );
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFOAuth2Manager.framework",
@@ -1299,7 +1312,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Jasonette/Pods-Jasonette-frameworks.sh\"\n";
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Jasonette/Pods-Jasonette-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@@ -1330,6 +1343,7 @@
EF07A20622D6186100D0D3FF /* JasonAgentService.m in Sources */,
EF07A1DF22D6186100D0D3FF /* JasonImageComponent.m in Sources */,
EF07A1F522D6186100D0D3FF /* JasonOauthAction.m in Sources */,
+ EF342844267C382D00AE3920 /* JasonATTrackingManager.m in Sources */,
EF07A20422D6186100D0D3FF /* JasonWebsocketService.m in Sources */,
EF07A1F422D6186100D0D3FF /* JasonCacheAction.m in Sources */,
EF07A1E922D6186100D0D3FF /* JasonNetworkAction.m in Sources */,
@@ -1380,6 +1394,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -1423,7 +1438,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "-fdiagnostics-show-option";
@@ -1436,6 +1451,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -1473,7 +1489,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "-fdiagnostics-show-option";
SDKROOT = iphoneos;
@@ -1496,7 +1512,7 @@
"$(PROJECT_DIR)",
);
INFOPLIST_FILE = Jasonette/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_CFLAGS = (
"$(inherited)",
@@ -1600,7 +1616,7 @@
"$(PROJECT_DIR)",
);
INFOPLIST_FILE = Jasonette/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.jasonette.seed;
PRODUCT_NAME = "$(TARGET_NAME)";
diff --git a/xcode/Jasonette/Actions/$audio/JasonAudioAction.m b/xcode/Jasonette/Actions/$audio/JasonAudioAction.m
index ec2e674d..2b78f68d 100644
--- a/xcode/Jasonette/Actions/$audio/JasonAudioAction.m
+++ b/xcode/Jasonette/Actions/$audio/JasonAudioAction.m
@@ -184,13 +184,13 @@ - (void)play {
commandCenter.nextTrackCommand.enabled = NO;
[commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus (MPRemoteCommandEvent * _Nonnull event) {
- [[Jason client] call:@{ @"type": @"$audio.pause" }];
- return MPRemoteCommandHandlerStatusSuccess;
- }];
+ [[Jason client] call:@{ @"type": @"$audio.pause" }];
+ return MPRemoteCommandHandlerStatusSuccess;
+ }];
[commandCenter.pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus (MPRemoteCommandEvent * _Nonnull event) {
- [[Jason client] call:@{ @"type": @"$audio.pause" }];
- return MPRemoteCommandHandlerStatusSuccess;
- }];
+ [[Jason client] call:@{ @"type": @"$audio.pause" }];
+ return MPRemoteCommandHandlerStatusSuccess;
+ }];
@@ -234,14 +234,14 @@ - (void)play {
}
completed:^(UIImage * i, NSError * error, SDImageCacheType cacheType, BOOL finished, NSURL * imageURL) {
MPMediaItemArtwork * albumArt = [[MPMediaItemArtwork alloc] initWithImage:i];
- [songInfo setObject:title
- forKey:MPMediaItemPropertyTitle];
- [songInfo setObject:author
- forKey:MPMediaItemPropertyArtist];
- [songInfo setObject:album
- forKey:MPMediaItemPropertyAlbumTitle];
- [songInfo setObject:albumArt
- forKey:MPMediaItemPropertyArtwork];
+ [songInfo setObject:title
+ forKey:MPMediaItemPropertyTitle];
+ [songInfo setObject:author
+ forKey:MPMediaItemPropertyArtist];
+ [songInfo setObject:album
+ forKey:MPMediaItemPropertyAlbumTitle];
+ [songInfo setObject:albumArt
+ forKey:MPMediaItemPropertyArtwork];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
}];
}
diff --git a/xcode/Jasonette/Actions/$geo/JasonGeoAction.m b/xcode/Jasonette/Actions/$geo/JasonGeoAction.m
index c86d6265..d18760e3 100644
--- a/xcode/Jasonette/Actions/$geo/JasonGeoAction.m
+++ b/xcode/Jasonette/Actions/$geo/JasonGeoAction.m
@@ -39,18 +39,18 @@ - (void)get {
delayUntilAuthorized:YES // This parameter is optional, defaults to NO if omitted
block:^(CLLocation * currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
if (status == INTULocationStatusSuccess) {
- // Request succeeded, meaning achievedAccuracy is at least the requested accuracy, and
- // currentLocation contains the device's current location.
- NSString * coord = [NSString stringWithFormat:@"%g,%g", currentLocation.coordinate.latitude, currentLocation.coordinate.longitude];
- [[Jason client] success:@{ @"coord": coord }];
+ // Request succeeded, meaning achievedAccuracy is at least the requested accuracy, and
+ // currentLocation contains the device's current location.
+ NSString * coord = [NSString stringWithFormat:@"%g,%g", currentLocation.coordinate.latitude, currentLocation.coordinate.longitude];
+ [[Jason client] success:@{ @"coord": coord }];
} else if (status == INTULocationStatusTimedOut) {
- // Wasn't able to locate the user with the requested accuracy within the timeout interval.
- // However, currentLocation contains the best location available (if any) as of right now,
- // and achievedAccuracy has info on the accuracy/recency of the location in currentLocation.
- NSString * coord = [NSString stringWithFormat:@"%g,%g", currentLocation.coordinate.latitude, currentLocation.coordinate.longitude];
- [[Jason client] success:@{ @"coord": coord }];
+ // Wasn't able to locate the user with the requested accuracy within the timeout interval.
+ // However, currentLocation contains the best location available (if any) as of right now,
+ // and achievedAccuracy has info on the accuracy/recency of the location in currentLocation.
+ NSString * coord = [NSString stringWithFormat:@"%g,%g", currentLocation.coordinate.latitude, currentLocation.coordinate.longitude];
+ [[Jason client] success:@{ @"coord": coord }];
} else {
- [[Jason client] error];
+ [[Jason client] error];
}
}];
}
diff --git a/xcode/Jasonette/Actions/$media/JasonMediaAction.m b/xcode/Jasonette/Actions/$media/JasonMediaAction.m
index 9aa3efb6..aea4ea15 100644
--- a/xcode/Jasonette/Actions/$media/JasonMediaAction.m
+++ b/xcode/Jasonette/Actions/$media/JasonMediaAction.m
@@ -218,19 +218,19 @@ - (void)imagePickerCallback:(NSDictionary *)info {
exportSession.outputURL = outputUrl;
exportSession.outputFileType = AVFileTypeMPEG4;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void)
- {
- NSString * contentType = @"video/mp4";
- NSData * mediaData = [NSData dataWithContentsOfURL:outputUrl];
- NSString * base64 = [mediaData base64EncodedStringWithOptions:0];
+ {
+ NSString * contentType = @"video/mp4";
+ NSData * mediaData = [NSData dataWithContentsOfURL:outputUrl];
+ NSString * base64 = [mediaData base64EncodedStringWithOptions:0];
- NSString * dataFormatString = @"data:video/mp4;base64,%@";
- NSString * dataString = [NSString stringWithFormat:dataFormatString, base64];
- NSURL * dataURI = [NSURL URLWithString:dataString];
+ NSString * dataFormatString = @"data:video/mp4;base64,%@";
+ NSString * dataString = [NSString stringWithFormat:dataFormatString, base64];
+ NSURL * dataURI = [NSURL URLWithString:dataString];
- NSDictionary * result = @{ @"file_url": url.absoluteString, @"data_uri": dataURI.absoluteString, @"data": base64, @"content_type": contentType };
- [[Jason client] success:result];
- }];
+ NSDictionary * result = @{ @"file_url": url.absoluteString, @"data_uri": dataURI.absoluteString, @"data": base64, @"content_type": contentType };
+ [[Jason client] success:result];
+ }];
} else if (isImage) {
UIImage * chosenImage;
diff --git a/xcode/Jasonette/Actions/$network/JasonNetworkAction.m b/xcode/Jasonette/Actions/$network/JasonNetworkAction.m
index 924a18bd..11a76942 100644
--- a/xcode/Jasonette/Actions/$network/JasonNetworkAction.m
+++ b/xcode/Jasonette/Actions/$network/JasonNetworkAction.m
@@ -106,14 +106,14 @@ - (void)request {
}
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self done:task
- for:url
- ofType:dataType
- with:responseObject
+ for:url
+ ofType:dataType
+ with:responseObject
original_url:original_url];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[weakSelf processError:error
- withOriginalUrl:original_url];
+ withOriginalUrl :original_url];
}];
return;
@@ -126,14 +126,14 @@ - (void)request {
}
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self done:task
- for:url
- ofType:dataType
- with:responseObject
+ for:url
+ ofType:dataType
+ with:responseObject
original_url:original_url];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[weakSelf processError:error
- withOriginalUrl:original_url];
+ withOriginalUrl :original_url];
}];
});
@@ -149,14 +149,14 @@ - (void)request {
headers:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
[self done:task
- for:url
- ofType:dataType
- with:responseObject
+ for:url
+ ofType:dataType
+ with:responseObject
original_url:original_url];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[weakSelf processError:error
- withOriginalUrl:original_url];
+ withOriginalUrl :original_url];
}];
});
return;
@@ -171,14 +171,14 @@ - (void)request {
headers:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
[self done:task
- for:url
- ofType:dataType
- with:responseObject
- original_url:original_url];
+ for:url
+ ofType:dataType
+ with:responseObject
+ original_url:original_url];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[weakSelf processError:error
- withOriginalUrl:original_url];
+ withOriginalUrl :original_url];
}];
});
@@ -194,14 +194,14 @@ - (void)request {
headers:nil
success:^(NSURLSessionDataTask * _Nonnull task) {
[self done:task
- for:url
- ofType:dataType
- with:nil
+ for:url
+ ofType:dataType
+ with:nil
original_url:original_url];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[weakSelf processError:error
- withOriginalUrl:original_url];
+ withOriginalUrl :original_url];
}];
});
@@ -217,14 +217,14 @@ - (void)request {
headers:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
[self done:task
- for:url
- ofType:dataType
- with:responseObject
- original_url:original_url];
+ for:url
+ ofType:dataType
+ with:responseObject
+ original_url:original_url];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[weakSelf processError:error
- withOriginalUrl:original_url];
+ withOriginalUrl :original_url];
}];
});
return;
@@ -244,14 +244,14 @@ - (void)request {
}
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self done:task
- for:url
- ofType:dataType
- with:responseObject
+ for:url
+ ofType:dataType
+ with:responseObject
original_url:original_url];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[weakSelf processError:error
- withOriginalUrl:original_url];
+ withOriginalUrl :original_url];
}];
});
}
@@ -370,15 +370,15 @@ - (void)_uploadData:(NSData *)mediaData ofType:(NSString *)contentType withFilen
}
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// Ignore if the url is different
- if (![JasonHelper isURL:task.originalRequest.URL
- equivalentTo :sign_url]) {
- return;
+ if (![JasonHelper isURL:task.originalRequest.URL
+ equivalentTo :sign_url]) {
+ return;
}
if (!responseObject[@"$jason"]) {
- [[Jason client] error:@{ @"description": @"The server must return a signed url wrapped with '$jason' key" }
- withOriginalUrl:original_url];
- return;
+ [[Jason client] error:@{ @"description": @"The server must return a signed url wrapped with '$jason' key" }
+ withOriginalUrl:original_url];
+ return;
}
NSMutableURLRequest * req = [[NSMutableURLRequest alloc] init];
@@ -387,31 +387,31 @@ - (void)_uploadData:(NSData *)mediaData ofType:(NSString *)contentType withFilen
[req setHTTPMethod:@"PUT"];
[req setURL:[NSURL URLWithString:responseObject[@"$jason"]]];
- NSURLSessionDataTask * upload_task = [manager dataTaskWithRequest:req
- uploadProgress:^(NSProgress * _Nonnull uploadProgress) {
- }
- downloadProgress:^(NSProgress * _Nonnull downloadProgress) {
- }
- completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
- if (!error) {
- dispatch_async (dispatch_get_main_queue (), ^{
- [self s3UploadDidSucceed:upload_filename
- withOriginalUrl:original_url];
- });
- } else {
- dispatch_async (dispatch_get_main_queue (), ^{
- DTLogWarning (@"error = %@", error);
- [self s3UploadDidFail:error
- withOriginalUrl:original_url];
- });
- }
- }];
+ NSURLSessionDataTask * upload_task = [manager dataTaskWithRequest:req
+ uploadProgress:^(NSProgress * _Nonnull uploadProgress) {
+ }
+ downloadProgress:^(NSProgress * _Nonnull downloadProgress) {
+ }
+ completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
+ if (!error) {
+ dispatch_async (dispatch_get_main_queue (), ^{
+ [self s3UploadDidSucceed:upload_filename
+ withOriginalUrl:original_url];
+ });
+ } else {
+ dispatch_async (dispatch_get_main_queue (), ^{
+ DTLogWarning (@"error = %@", error);
+ [self s3UploadDidFail:error
+ withOriginalUrl:original_url];
+ });
+ }
+ }];
[upload_task resume];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- [self s3UploadDidFail:error
- withOriginalUrl:original_url];
+ [self s3UploadDidFail:error
+ withOriginalUrl:original_url];
}];
}
- (void)s3UploadDidFail:(NSError *)error withOriginalUrl:(NSString *)original_url
diff --git a/xcode/Jasonette/Actions/$oauth/JasonOauthAction.m b/xcode/Jasonette/Actions/$oauth/JasonOauthAction.m
index ec22c44a..38e47e12 100644
--- a/xcode/Jasonette/Actions/$oauth/JasonOauthAction.m
+++ b/xcode/Jasonette/Actions/$oauth/JasonOauthAction.m
@@ -9,6 +9,7 @@
#import
#import
#import "JasonNetworking.h"
+#import "JasonLogger.h"
#pragma message "TODO: Update managers to use JasonNetworking"
@@ -108,9 +109,9 @@ - (void)request {
if ([host containsString:@"facebook"]) {
options = @{
- ACFacebookAppIdKey: client_id,
- // ACFacebookPermissionsKey: @[@"user_friends", @"email"],
- ACFacebookAudienceKey: ACFacebookAudienceFriends
+ ACFacebookAppIdKey: client_id,
+ // ACFacebookPermissionsKey: @[@"user_friends", @"email"],
+ ACFacebookAudienceKey: ACFacebookAudienceFriends
};
}
@@ -131,50 +132,50 @@ - (void)request {
options:options
completion:^(BOOL granted, NSError * error) {
if (granted == YES) {
- NSArray * arrayOfAccounts = [account accountsWithAccountType:accountType];
-
- if ([arrayOfAccounts count] > 0) {
- dispatch_async (dispatch_get_main_queue (), ^{
- AHKActionSheet * actionSheet = [[AHKActionSheet alloc] initWithTitle:nil];
-
- for (int i = 0; i < arrayOfAccounts.count; i++) {
- ACAccount * a = arrayOfAccounts[i];
- [actionSheet addButtonWithTitle:a.username
- type:AHKActionSheetButtonTypeDefault
- handler:^(AHKActionSheet * as) {
- dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
- UICKeyChainStore * keychain = [UICKeyChainStore keyChainStoreWithService:@"$socialframework"];
- [keychain setString:a.identifier
- forKey:client_id];
- [self performSocialFrameworkRequestFor:a];
- });
- }];
- }
-
- actionSheet.title = @"Select an account";
- actionSheet.blurTintColor = [UIColor colorWithWhite:1.0f
- alpha:0.75f];
- actionSheet.blurRadius = 8.0f;
- actionSheet.buttonHeight = 45.0f;
-
- actionSheet.animationDuration = 0.2f;
- UIFont * defaultFont = [UIFont fontWithName:@"HelveticaNeue"
- size:16.0f];
- actionSheet.buttonTextAttributes = @{ NSFontAttributeName: defaultFont,
- NSForegroundColorAttributeName: [UIColor blackColor] };
- actionSheet.disabledButtonTextAttributes = @{ NSFontAttributeName: defaultFont,
- NSForegroundColorAttributeName: [UIColor grayColor] };
- actionSheet.destructiveButtonTextAttributes = @{ NSFontAttributeName: defaultFont,
- NSForegroundColorAttributeName: [UIColor redColor] };
- actionSheet.cancelButtonTextAttributes = @{ NSFontAttributeName: defaultFont,
- NSForegroundColorAttributeName: [UIColor blackColor] };
- [actionSheet show];
- });
+ NSArray * arrayOfAccounts = [account accountsWithAccountType:accountType];
+
+ if ([arrayOfAccounts count] > 0) {
+ dispatch_async (dispatch_get_main_queue (), ^{
+ AHKActionSheet * actionSheet = [[AHKActionSheet alloc] initWithTitle:nil];
+
+ for (int i = 0; i < arrayOfAccounts.count; i++) {
+ ACAccount * a = arrayOfAccounts[i];
+ [actionSheet addButtonWithTitle:a.username
+ type:AHKActionSheetButtonTypeDefault
+ handler:^(AHKActionSheet * as) {
+ dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
+ UICKeyChainStore * keychain = [UICKeyChainStore keyChainStoreWithService:@"$socialframework"];
+ [keychain setString:a.identifier
+ forKey:client_id];
+ [self performSocialFrameworkRequestFor:a];
+ });
+ }];
+ }
+
+ actionSheet.title = @"Select an account";
+ actionSheet.blurTintColor = [UIColor colorWithWhite:1.0f
+ alpha:0.75f];
+ actionSheet.blurRadius = 8.0f;
+ actionSheet.buttonHeight = 45.0f;
+
+ actionSheet.animationDuration = 0.2f;
+ UIFont * defaultFont = [UIFont fontWithName:@"HelveticaNeue"
+ size:16.0f];
+ actionSheet.buttonTextAttributes = @{ NSFontAttributeName: defaultFont,
+ NSForegroundColorAttributeName: [UIColor blackColor] };
+ actionSheet.disabledButtonTextAttributes = @{ NSFontAttributeName: defaultFont,
+ NSForegroundColorAttributeName: [UIColor grayColor] };
+ actionSheet.destructiveButtonTextAttributes = @{ NSFontAttributeName: defaultFont,
+ NSForegroundColorAttributeName: [UIColor redColor] };
+ actionSheet.cancelButtonTextAttributes = @{ NSFontAttributeName: defaultFont,
+ NSForegroundColorAttributeName: [UIColor blackColor] };
+ [actionSheet show];
+ });
+ } else {
+ [[Jason client] error];
+ }
} else {
- [[Jason client] error];
- }
- } else {
- [[Jason client] error];
+ [[Jason client] error];
}
}];
}
@@ -326,10 +327,10 @@ - (void)_process {
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (!error) {
- [[Jason client] success:responseObject];
+ [[Jason client] success:responseObject];
} else {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [[Jason client] error];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [[Jason client] error];
}
}] resume];
}
@@ -388,32 +389,32 @@ - (void)_process {
BOOL same = YES;
if ([originalRequestPath isEqualToString:path]) {
- if (task.originalRequest.URL.query && task.originalRequest.URL.query.length > 0) {
- NSArray * originalRequestQueries = [task.originalRequest.URL.query componentsSeparatedByString:@"&"];
-
- for (NSString * kv in originalRequestQueries) {
- NSArray * pairComponents = [kv componentsSeparatedByString:@"="];
- NSString * key = [[pairComponents firstObject] stringByRemovingPercentEncoding];
- NSString * value = [[pairComponents lastObject] stringByRemovingPercentEncoding];
-
- // check if the value is the same as the original
- if (![parameters[key] isEqualToString:value]) {
- same = NO;
- break;
- }
- }
- }
+ if (task.originalRequest.URL.query && task.originalRequest.URL.query.length > 0) {
+ NSArray * originalRequestQueries = [task.originalRequest.URL.query componentsSeparatedByString:@"&"];
+
+ for (NSString * kv in originalRequestQueries) {
+ NSArray * pairComponents = [kv componentsSeparatedByString:@"="];
+ NSString * key = [[pairComponents firstObject] stringByRemovingPercentEncoding];
+ NSString * value = [[pairComponents lastObject] stringByRemovingPercentEncoding];
+
+ // check if the value is the same as the original
+ if (![parameters[key] isEqualToString:value]) {
+ same = NO;
+ break;
+ }
+ }
+ }
} else {
- same = NO;
+ same = NO;
}
if (same) {
- [[Jason client] success:responseObject];
+ [[Jason client] success:responseObject];
}
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- NSString * ErrorResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]
- encoding:NSUTF8StringEncoding];
+ NSString * ErrorResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]
+ encoding:NSUTF8StringEncoding];
NSLog (@"#E = %@", ErrorResponse);
[[Jason client] error];
}];
@@ -430,19 +431,19 @@ - (void)_process {
NSString * originalRequestQuery = nil;
if (task.originalRequest.URL.query && task.originalRequest.URL.query.length > 0) {
- originalRequestQuery = [task.originalRequest.URL.query stringByRemovingPercentEncoding];
+ originalRequestQuery = [task.originalRequest.URL.query stringByRemovingPercentEncoding];
}
NSString * originalRequestFullPath;
if (originalRequestQuery) {
- originalRequestFullPath = [NSString stringWithFormat:@"%@?%@", originalRequestPath, originalRequestQuery];
+ originalRequestFullPath = [NSString stringWithFormat:@"%@?%@", originalRequestPath, originalRequestQuery];
} else {
- originalRequestFullPath = originalRequestPath;
+ originalRequestFullPath = originalRequestPath;
}
if (![originalRequestFullPath isEqualToString:path]) {
- return;
+ return;
}
[[Jason client] success:responseObject];
@@ -460,19 +461,19 @@ - (void)_process {
NSString * originalRequestQuery = nil;
if (task.originalRequest.URL.query && task.originalRequest.URL.query.length > 0) {
- originalRequestQuery = [task.originalRequest.URL.query stringByRemovingPercentEncoding];
+ originalRequestQuery = [task.originalRequest.URL.query stringByRemovingPercentEncoding];
}
NSString * originalRequestFullPath;
if (originalRequestQuery) {
- originalRequestFullPath = [NSString stringWithFormat:@"%@?%@", originalRequestPath, originalRequestQuery];
+ originalRequestFullPath = [NSString stringWithFormat:@"%@?%@", originalRequestPath, originalRequestQuery];
} else {
- originalRequestFullPath = originalRequestPath;
+ originalRequestFullPath = originalRequestPath;
}
if (![originalRequestFullPath isEqualToString:path]) {
- return;
+ return;
}
[[Jason client] success:responseObject];
@@ -490,19 +491,19 @@ - (void)_process {
NSString * originalRequestQuery = nil;
if (task.originalRequest.URL.query && task.originalRequest.URL.query.length > 0) {
- originalRequestQuery = [task.originalRequest.URL.query stringByRemovingPercentEncoding];
+ originalRequestQuery = [task.originalRequest.URL.query stringByRemovingPercentEncoding];
}
NSString * originalRequestFullPath;
if (originalRequestQuery) {
- originalRequestFullPath = [NSString stringWithFormat:@"%@?%@", originalRequestPath, originalRequestQuery];
+ originalRequestFullPath = [NSString stringWithFormat:@"%@?%@", originalRequestPath, originalRequestQuery];
} else {
- originalRequestFullPath = originalRequestPath;
+ originalRequestFullPath = originalRequestPath;
}
if (![originalRequestFullPath isEqualToString:path]) {
- return;
+ return;
}
[[Jason client] success];
@@ -520,19 +521,19 @@ - (void)_process {
NSString * originalRequestQuery = nil;
if (task.originalRequest.URL.query && task.originalRequest.URL.query.length > 0) {
- originalRequestQuery = [task.originalRequest.URL.query stringByRemovingPercentEncoding];
+ originalRequestQuery = [task.originalRequest.URL.query stringByRemovingPercentEncoding];
}
NSString * originalRequestFullPath;
if (originalRequestQuery) {
- originalRequestFullPath = [NSString stringWithFormat:@"%@?%@", originalRequestPath, originalRequestQuery];
+ originalRequestFullPath = [NSString stringWithFormat:@"%@?%@", originalRequestPath, originalRequestQuery];
} else {
- originalRequestFullPath = originalRequestPath;
+ originalRequestFullPath = originalRequestPath;
}
if (![originalRequestFullPath isEqualToString:path]) {
- return;
+ return;
}
[[Jason client] success:responseObject];
@@ -559,17 +560,17 @@ - (void)access_token {
if ([host containsString:@"facebook"]) {
options = @{
- ACFacebookAppIdKey: client_id,
- // ACFacebookPermissionsKey: @[@"user_friends", @"email"],
- ACFacebookAudienceKey: ACFacebookAudienceFriends
+ ACFacebookAppIdKey: client_id,
+ // ACFacebookPermissionsKey: @[@"user_friends", @"email"],
+ ACFacebookAudienceKey: ACFacebookAudienceFriends
};
[accountStore requestAccessToAccountsWithType:accountTypeFacebook
options:options
completion:^(BOOL granted, NSError * error) {
if (granted) {
- NSArray * accounts = [accountStore accountsWithAccountType:accountTypeFacebook];
- ACAccount * account = [accounts lastObject];
- [[Jason client] success:@{ @"token": account.credential.oauthToken }];
+ NSArray * accounts = [accountStore accountsWithAccountType:accountTypeFacebook];
+ ACAccount * account = [accounts lastObject];
+ [[Jason client] success:@{ @"token": account.credential.oauthToken }];
}
}];
}
@@ -612,8 +613,8 @@ - (void)refresh_token:(NSString *)provider {
refreshToken:credential.refreshToken
success:^(AFOAuthCredential * credential) {
NSLog (@"Success! your new credential is %@", credential);
- [AFOAuthCredential storeCredential:credential
- withIdentifier:client_id];
+ [AFOAuthCredential storeCredential:credential
+ withIdentifier:client_id];
[[Jason client] success];
}
failure:^(NSError * error) {
@@ -706,50 +707,54 @@ - (void)auth {
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
// Ignore if the url is different
if (![request.URL.absoluteString isEqualToString:response.URL.absoluteString]) {
- return;
+ return;
}
if (!error) {
- dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
- NSString * s = [[NSString alloc] initWithData:responseObject
- encoding:NSUTF8StringEncoding];
-
- NSString * urlToParse = [NSString stringWithFormat:@"http://localhost?%@", s];
- NSArray * queryItems = [self extractQueryParams:urlToParse];
- NSString * oauth_token = [self valueForKey:@"oauth_token"
- fromQueryItems :queryItems];
- NSString * oauth_token_secret = [self valueForKey:@"oauth_token_secret"
- fromQueryItems :queryItems];
- NSString * authorize_url;
-
- if (oauth_token_secret) {
- self.cache = [@{ @"oauth1_three_legged_secret": oauth_token_secret } mutableCopy];
- authorize_url = [NSString stringWithFormat:@"%@://%@%@?oauth_token=%@&oauth_token_secret=%@", authorize_options[@"scheme"], authorize_options[@"host"], authorize_options[@"path"], oauth_token, oauth_token_secret];
+ dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
+ NSString * s = [[NSString alloc] initWithData:responseObject
+ encoding:NSUTF8StringEncoding];
+
+ NSString * urlToParse = [NSString stringWithFormat:@"http://localhost?%@", s];
+ NSArray * queryItems = [self extractQueryParams:urlToParse];
+ NSString * oauth_token = [self valueForKey:@"oauth_token"
+ fromQueryItems :queryItems];
+ NSString * oauth_token_secret = [self valueForKey:@"oauth_token_secret"
+ fromQueryItems :queryItems];
+ NSString * authorize_url;
+
+ if (oauth_token_secret) {
+ self.cache = [@{ @"oauth1_three_legged_secret": oauth_token_secret } mutableCopy];
+ authorize_url = [NSString stringWithFormat:@"%@://%@%@?oauth_token=%@&oauth_token_secret=%@", authorize_options[@"scheme"], authorize_options[@"host"], authorize_options[@"path"], oauth_token, oauth_token_secret];
+ } else {
+ self.cache = nil;
+ authorize_url = [NSString stringWithFormat:@"%@://%@%@?oauth_token=%@", authorize_options[@"scheme"], authorize_options[@"host"], authorize_options[@"path"], oauth_token];
+ }
+
+ dispatch_async (dispatch_get_main_queue (), ^{
+ NSURL * URL = [NSURL URLWithString:authorize_url];
+ NSString * view = authorize_options[@"view"];
+
+ if (view && [view isEqualToString:@"app"]) {
+ // Launch external safari for oauth
+ [[UIApplication sharedApplication] openURL:URL
+ options:@{}
+ completionHandler:^(BOOL success) {
+ DTLogDebug (@"Openned %@", URL);
+ }];
+ } else {
+ // By default use SFSafariViewController
+ SFSafariViewController * vc = [[SFSafariViewController alloc] initWithURL:URL];
+ vc.delegate = self;
+ [self.VC presentViewController:vc
+ animated:NO
+ completion:^{ }];
+ }
+ });
+ });
} else {
- self.cache = nil;
- authorize_url = [NSString stringWithFormat:@"%@://%@%@?oauth_token=%@", authorize_options[@"scheme"], authorize_options[@"host"], authorize_options[@"path"], oauth_token];
- }
-
- dispatch_async (dispatch_get_main_queue (), ^{
- NSURL * URL = [NSURL URLWithString:authorize_url];
- NSString * view = authorize_options[@"view"];
-
- if (view && [view isEqualToString:@"app"]) {
- // Launch external safari for oauth
- [[UIApplication sharedApplication] openURL:URL];
- } else {
- // By default use SFSafariViewController
- SFSafariViewController * vc = [[SFSafariViewController alloc] initWithURL:URL];
- vc.delegate = self;
- [self.VC presentViewController:vc
- animated:NO
- completion:^{ }];
- }
- });
- });
- } else {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [[Jason client] error];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [[Jason client] error];
}
}];
@@ -792,11 +797,11 @@ - (void)auth {
NSDictionary * access_options = self.options[@"access"];
if ([access_options[@"data"][@"grant_type"] isEqualToString:@"password"]) {
- /********************************************************************************
- *
- * Case 1: Password type
- *
- ********************************************************************************/
+ /********************************************************************************
+ *
+ * Case 1: Password type
+ *
+ ********************************************************************************/
NSString * client_id = self.options[@"access"][@"client_id"];
NSString * client_secret = self.options[@"access"][@"client_secret"];
@@ -844,15 +849,15 @@ - (void)auth {
NSString * refresh_token = responseObject[@"refresh_token"];
if (access_token) {
- NSString * token_type = responseObject[@"token_type"];
- AFOAuthCredential * credential = [AFOAuthCredential credentialWithOAuthToken:access_token
- tokenType:token_type];
- credential.refreshToken = refresh_token;
- [AFOAuthCredential storeCredential:credential
- withIdentifier:client_id];
- [[Jason client] success];
+ NSString * token_type = responseObject[@"token_type"];
+ AFOAuthCredential * credential = [AFOAuthCredential credentialWithOAuthToken:access_token
+ tokenType:token_type];
+ credential.refreshToken = refresh_token;
+ [AFOAuthCredential storeCredential:credential
+ withIdentifier:client_id];
+ [[Jason client] success];
} else {
- [[Jason client] error];
+ [[Jason client] error];
}
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
@@ -885,7 +890,7 @@ - (void)auth {
if (credential && credential.isExpired && credential.refreshToken) {
[self refresh_token:client_id];
} else {
- // Generate the final url (U) from scheme/host/path/data
+ // Generate the final url (U) from scheme/host/path/data
NSString * url = [NSString stringWithFormat:@"%@://%@%@", authorize_options[@"scheme"], authorize_options[@"host"], authorize_options[@"path"]];
NSMutableDictionary * parameters = [authorize_options[@"data"] mutableCopy];
NSURLComponents * components = [NSURLComponents componentsWithString:url];
@@ -908,7 +913,11 @@ - (void)auth {
if (view && [view isEqualToString:@"app"]) {
// Launch external safari for oauth
- [[UIApplication sharedApplication] openURL:U];
+ [[UIApplication sharedApplication] openURL:U
+ options:@{}
+ completionHandler:^(BOOL success) {
+ DTLogDebug (@"Openned %@", U);
+ }];
} else {
// By default use SFSafariViewController
SFSafariViewController * vc = [[SFSafariViewController alloc] initWithURL:U];
@@ -1007,34 +1016,34 @@ - (void)oauth_callback:(NSNotification *)notification {
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
// Ignore if the url is different
if (![request.URL.absoluteString isEqualToString:response.URL.absoluteString]) {
- return;
+ return;
}
if (!error) {
- // Temp url just to take advantage of componentsWithURL
- NSString * s = [[NSString alloc] initWithData:responseObject
- encoding:NSUTF8StringEncoding];
- NSString * urlToParse = [NSString stringWithFormat:@"http://localhost?%@", s];
- NSURLComponents * components = [NSURLComponents componentsWithURL:[NSURL URLWithString:urlToParse]
- resolvingAgainstBaseURL :NO];
- NSArray * queryItems = [components queryItems];
- NSString * oauth_token = [self valueForKey:@"oauth_token"
- fromQueryItems :queryItems];
- NSString * oauth_token_secret = [self valueForKey:@"oauth_token_secret"
- fromQueryItems :queryItems];
-
-
- NSString * APP_NAME = [[NSBundle mainBundle] bundleIdentifier];
-
- UICKeyChainStore * keychain = [UICKeyChainStore keyChainStoreWithService:[NSString stringWithFormat:@"%@", APP_NAME]];
- [keychain setString:oauth_token
- forKey:[NSString stringWithFormat:@"%@#token", client_id]];
- [keychain setString:oauth_token_secret
- forKey:[NSString stringWithFormat:@"%@#secret", client_id]];
- [[Jason client] success];
+ // Temp url just to take advantage of componentsWithURL
+ NSString * s = [[NSString alloc] initWithData:responseObject
+ encoding:NSUTF8StringEncoding];
+ NSString * urlToParse = [NSString stringWithFormat:@"http://localhost?%@", s];
+ NSURLComponents * components = [NSURLComponents componentsWithURL:[NSURL URLWithString:urlToParse]
+ resolvingAgainstBaseURL :NO];
+ NSArray * queryItems = [components queryItems];
+ NSString * oauth_token = [self valueForKey:@"oauth_token"
+ fromQueryItems :queryItems];
+ NSString * oauth_token_secret = [self valueForKey:@"oauth_token_secret"
+ fromQueryItems :queryItems];
+
+
+ NSString * APP_NAME = [[NSBundle mainBundle] bundleIdentifier];
+
+ UICKeyChainStore * keychain = [UICKeyChainStore keyChainStoreWithService:[NSString stringWithFormat:@"%@", APP_NAME]];
+ [keychain setString:oauth_token
+ forKey:[NSString stringWithFormat:@"%@#token", client_id]];
+ [keychain setString:oauth_token_secret
+ forKey:[NSString stringWithFormat:@"%@#secret", client_id]];
+ [[Jason client] success];
} else {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [[Jason client] error];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [[Jason client] error];
}
}];
@@ -1149,15 +1158,15 @@ - (void)oauth_callback:(NSNotification *)notification {
[OAuth2Manager authenticateUsingOAuthWithURLString:access_options[@"path"]
parameters:access_data
success:^(AFOAuthCredential * credential) {
- [AFOAuthCredential storeCredential:credential
- withIdentifier:client_id];
+ [AFOAuthCredential storeCredential:credential
+ withIdentifier:client_id];
[[Jason client] success];
}
failure:^(NSError * error) {
NSLog (@"Error: %@", error);
- NSString * ErrorResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]
- encoding:NSUTF8StringEncoding];
+ NSString * ErrorResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]
+ encoding:NSUTF8StringEncoding];
NSLog (@"#ncoded = %@", ErrorResponse);
[[Jason client] error];
diff --git a/xcode/Jasonette/Actions/$push/JasonPushAction.m b/xcode/Jasonette/Actions/$push/JasonPushAction.m
index 36ea2b81..1da6a1b6 100644
--- a/xcode/Jasonette/Actions/$push/JasonPushAction.m
+++ b/xcode/Jasonette/Actions/$push/JasonPushAction.m
@@ -192,35 +192,27 @@ - (void)register {
// currently only remote notification
#ifdef PUSH
- if (SYSTEM_VERSION_GRATERTHAN_OR_EQUALTO (@"10.0")) {
- UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
- JasonPushService * service = [Jason client].services[@"JasonPushService"];
+ // iOS >= 10 Push only
+ UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
+ JasonPushService * service = [Jason client].services[@"JasonPushService"];
- if (service) {
- center.delegate = service;
- }
-
- [center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge)
- completionHandler:^(BOOL granted, NSError * _Nullable error)
- {
- if (!error && granted) {
- [[UIApplication sharedApplication] registerForRemoteNotifications];
- [[Jason client] success];
- } else {
- [[Jason client] error];
- }
- }];
- } else {
- [[UIApplication sharedApplication]
- registerUserNotificationSettings:[UIUserNotificationSettings
- settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge)
- categories:nil]];
-
- [[UIApplication sharedApplication] registerForRemoteNotifications];
-
- [[Jason client] success];
+ if (service) {
+ center.delegate = service;
}
+ [center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge)
+ completionHandler:^(BOOL granted, NSError * _Nullable error)
+ {
+ if (!error && granted) {
+ dispatch_async (dispatch_get_main_queue (), ^{
+ [[UIApplication sharedApplication] registerForRemoteNotifications];
+ });
+ [[Jason client] success];
+ } else {
+ [[Jason client] error];
+ }
+ }];
+
#else /* ifdef PUSH */
DTLogWarning (@"Push notification turned off by default. If you'd like to suport push, uncomment the #define statement in Constants.h and turn on the push notification feature from the capabilities tab.");
#endif /* ifdef PUSH */
diff --git a/xcode/Jasonette/Actions/$script/JasonScriptAction.m b/xcode/Jasonette/Actions/$script/JasonScriptAction.m
index a9ac6e75..fa9fd44b 100644
--- a/xcode/Jasonette/Actions/$script/JasonScriptAction.m
+++ b/xcode/Jasonette/Actions/$script/JasonScriptAction.m
@@ -44,8 +44,8 @@ - (void)include {
progress:^(NSProgress * _Nonnull downloadProgress) { }
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSString * js = [JasonHelper UTF8StringFromData:((NSData *)responseObject)];
- [self inject:js
- into:context];
+ [self inject:js
+ into:context];
dispatch_group_leave (requireGroup);
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
diff --git a/xcode/Jasonette/Actions/$util/JasonUtilAction.m b/xcode/Jasonette/Actions/$util/JasonUtilAction.m
index d265b8df..45c5fb23 100644
--- a/xcode/Jasonette/Actions/$util/JasonUtilAction.m
+++ b/xcode/Jasonette/Actions/$util/JasonUtilAction.m
@@ -165,16 +165,16 @@ - (void)alert {
DTLogWarning (@"Alert OK");
if (form && form.count > 0) {
- for (NSString * input_name in textFields) {
- UITextField * textField = (UITextField *)textFields[input_name];
- [form_inputs setObject:textField.text
- forKey:input_name];
- }
-
- DTLogDebug (@"Sending Form Inputs %@", form_inputs);
- [[Jason client] success:form_inputs];
+ for (NSString * input_name in textFields) {
+ UITextField * textField = (UITextField *)textFields[input_name];
+ [form_inputs setObject:textField.text
+ forKey:input_name];
+ }
+
+ DTLogDebug (@"Sending Form Inputs %@", form_inputs);
+ [[Jason client] success:form_inputs];
} else {
- [[Jason client] success];
+ [[Jason client] success];
}
}];
@@ -183,8 +183,8 @@ - (void)alert {
handler:^(UIAlertAction * action) {
DTLogWarning (@"Alert Cancel");
[[Jason client] error];
- [alert dismissViewControllerAnimated:YES
- completion:nil];
+ [alert dismissViewControllerAnimated:YES
+ completion:nil];
}];
if (cancelButtonEnabled) {
@@ -222,13 +222,13 @@ - (void)share {
}
completed:^(UIImage * image, NSError * error, SDImageCacheType cacheType, BOOL finished, NSURL * imageURL) {
if (image) {
- [share_items addObject:image];
+ [share_items addObject:image];
}
counter--;
if (counter == 0) {
- [self openShareWith:share_items];
+ [self openShareWith:share_items];
}
}];
} else if (file_url) {
@@ -347,8 +347,8 @@ - (void)openShareWith:(NSArray *)items {
// Present the controller
[controller setCompletionWithItemsHandler:
^(NSString * activityType, BOOL completed, NSArray * returnedItems, NSError * activityError) {
- [[Jason client] success];
- }];
+ [[Jason client] success];
+ }];
dispatch_async (dispatch_get_main_queue (), ^{
[self.VC.navigationController presentViewController:controller animated:YES completion:nil];
@@ -368,9 +368,9 @@ - (void)picker {
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (item[@"href"]) {
- [[Jason client] go:item[@"href"]];
+ [[Jason client] go:item[@"href"]];
} else if (item[@"action"]) {
- [[Jason client] call:item[@"action"]];
+ [[Jason client] call:item[@"action"]];
}
}];
[alert addAction:action];
@@ -422,6 +422,7 @@ - (void)datepicker {
// Create date selection view controller
RMDateSelectionViewController * dateSelectionController = [RMDateSelectionViewController actionControllerWithStyle:style selectAction:selectAction andCancelAction:cancelAction];
+
dateSelectionController.title = title;
dateSelectionController.message = description;
@@ -449,19 +450,19 @@ - (void)addressbook {
case APAddressBookAccessUnknown: {
// Application didn't request address book access yet
[addressbook requestAccess:^(BOOL granted, NSError * error)
- {
- if (error) {
- DTLogWarning (@"%@", error);
- [[Jason client] error];
- } else {
- if (granted) {
- [self fetchAddressbook:addressbook];
- } else {
- DTLogWarning (@"%@", error);
- [[Jason client] error];
- }
- }
- }];
+ {
+ if (error) {
+ DTLogWarning (@"%@", error);
+ [[Jason client] error];
+ } else {
+ if (granted) {
+ [self fetchAddressbook:addressbook];
+ } else {
+ DTLogWarning (@"%@", error);
+ [[Jason client] error];
+ }
+ }
+ }];
break;
}
@@ -488,24 +489,24 @@ - (void)fetchAddressbook:(APAddressBook *)addressbook {
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[addressbook loadContacts:^(NSArray * contacts, NSError * error)
- {
- // hide activity
- if (!error) {
- // do something with contacts array
- NSMutableArray * result = [NSMutableArray array];
- [contacts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
- [result addObject:[NSDictionary dictionaryWithObjects:@[[self contactName:obj], [self contactPhones:obj], [self contactEmails:obj]]
- forKeys:@[@"name", @"phone", @"email"]]];
- }];
-
- DTLogDebug (@"Contacts %@", result);
- [[Jason client] success:result];
- } else {
- // show error
- DTLogDebug (@"%@", error);
- [[Jason client] error];
- }
- }];
+ {
+ // hide activity
+ if (!error) {
+ // do something with contacts array
+ NSMutableArray * result = [NSMutableArray array];
+ [contacts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
+ [result addObject:[NSDictionary dictionaryWithObjects:@[[self contactName:obj], [self contactPhones:obj], [self contactEmails:obj]]
+ forKeys:@[@"name", @"phone", @"email"]]];
+ }];
+
+ DTLogDebug (@"Contacts %@", result);
+ [[Jason client] success:result];
+ } else {
+ // show error
+ DTLogDebug (@"%@", error);
+ [[Jason client] error];
+ }
+ }];
});
}
diff --git a/xcode/Jasonette/Components/button/JasonButtonComponent.m b/xcode/Jasonette/Components/button/JasonButtonComponent.m
index 8b00fbda..7ea3381a 100644
--- a/xcode/Jasonette/Components/button/JasonButtonComponent.m
+++ b/xcode/Jasonette/Components/button/JasonButtonComponent.m
@@ -85,20 +85,20 @@ + (UIView *)build:(UIButton *)component withJSON:(NSDictionary *)json withOption
placeholderImage:placeholder_image
completed:^(UIImage * i, NSError * error, SDImageCacheType cacheType, NSURL * imageURL) {
if (!error) {
- JasonComponentFactory.imageLoaded[url] = [NSValue valueWithCGSize:i.size];
-
- if (style[@"color"]) {
- NSString * colorHex = style[@"color"];
- UIColor * c = [JasonHelper colorwithHexString:colorHex
- alpha:1.0];
- UIImage * image = [imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
- [component setTintColor:c];
- [component setImage:image
- forState:UIControlStateNormal];
- } else {
- [component setImage:imageView.image
- forState:UIControlStateNormal];
- }
+ JasonComponentFactory.imageLoaded[url] = [NSValue valueWithCGSize:i.size];
+
+ if (style[@"color"]) {
+ NSString * colorHex = style[@"color"];
+ UIColor * c = [JasonHelper colorwithHexString:colorHex
+ alpha:1.0];
+ UIImage * image = [imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ [component setTintColor:c];
+ [component setImage:image
+ forState:UIControlStateNormal];
+ } else {
+ [component setImage:imageView.image
+ forState:UIControlStateNormal];
+ }
}
}];
}
diff --git a/xcode/Jasonette/Components/html/JasonHtmlComponent.m b/xcode/Jasonette/Components/html/JasonHtmlComponent.m
index dca1ac8c..e2ee3230 100644
--- a/xcode/Jasonette/Components/html/JasonHtmlComponent.m
+++ b/xcode/Jasonette/Components/html/JasonHtmlComponent.m
@@ -10,27 +10,24 @@
@implementation JasonHtmlComponent
+ (UIView *)build:(WKWebView *)component withJSON:(NSDictionary *)json withOptions:(NSDictionary *)options {
-
if (!component) {
-
WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc] init];
-
+
[config setAllowsInlineMediaPlayback:YES];
-
+
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
+
if ([config respondsToSelector:@selector(setMediaPlaybackRequiresUserAction:)]) {
[config setMediaPlaybackRequiresUserAction:NO];
}
-
+
#pragma clang diagnostic pop
-
+
if (@available(iOS 10, *)) {
[config setMediaTypesRequiringUserActionForPlayback:WKAudiovisualMediaTypeNone];
}
-
-
+
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
CGFloat height = [[UIScreen mainScreen] bounds].size.height;
@@ -43,21 +40,20 @@ + (UIView *)build:(WKWebView *)component withJSON:(NSDictionary *)json withOptio
height = [JasonHelper pixelsInDirection:@"vertical" fromExpression:json[@"style"][@"height"]];
}
}
-
-
+
// TODO: Figure it out what this does
NSString * summon = @"var JASON={call: function(e){var n=document.createElement(\"IFRAME\");n.setAttribute(\"src\",\"jason:\"+JSON.stringify(e)),document.documentElement.appendChild(n),n.parentNode.removeChild(n),n=null}};";
-
+
WKUserScript * summonScript = [[WKUserScript alloc] initWithSource:summon injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
-
+
WKUserContentController * controller = [WKUserContentController new];
[controller addUserScript:summonScript];
config.userContentController = controller;
-
+
CGRect frame = CGRectMake (0, 0, width, height);
component = [[WKWebView alloc] initWithFrame:frame configuration:config];
-
+
component.translatesAutoresizingMaskIntoConstraints = NO;
component.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
component.navigationDelegate = [self self];
@@ -67,39 +63,35 @@ + (UIView *)build:(WKWebView *)component withJSON:(NSDictionary *)json withOptio
component.backgroundColor = [UIColor clearColor];
if (json[@"text"] && ![[NSNull null] isEqual:json[@"text"]]) {
-
// Remember to add
// if you want to display properly
NSString * html = [json[@"text"] description];
NSString * lowerCaseHtml = [html lowercaseString];
-
- if(![lowerCaseHtml containsString:@"viewport"] && ![lowerCaseHtml
- containsString:@"initial-scale"])
- {
- DTLogWarning(@"html not found. Display could be not properly rendered in html component. Forcing initial-scale=1.0.");
-
+
+ if (![lowerCaseHtml containsString:@"viewport"] && ![lowerCaseHtml
+ containsString:@"initial-scale"]) {
+ DTLogWarning (@"html not found. Display could be not properly rendered in html component. Forcing initial-scale=1.0.");
+
NSRange headRange = [[html lowercaseString] rangeOfString:@""];
- DTLogDebug(@"Range location %d length %d", headRange.location, headRange.length);
+ DTLogDebug (@"Range location %d length %d", headRange.location, headRange.length);
- if(headRange.location != NSNotFound) {
-
+ if (headRange.location != NSNotFound) {
NSString * head = [[html substringToIndex:headRange.location] stringByAppendingString:@""];
-
- DTLogDebug(@"Head", head);
-
+
+ DTLogDebug (@"Head", head);
+
NSString * body = [html substringFromIndex:(headRange.location + @"".length)];
-
- DTLogDebug(@"Body", body);
-
+
+ DTLogDebug (@"Body", body);
+
html = [NSString stringWithFormat:@"%@%@", head, body];
}
-
}
-
- DTLogDebug(@"Rendering HTML Component %@", html);
-
+
+ DTLogDebug (@"Rendering HTML Component %@", html);
+
[component loadHTMLString:html baseURL:nil];
-
+
component.scrollView.scrollEnabled = NO;
//component.delegate = [self self];
@@ -114,7 +106,6 @@ + (UIView *)build:(WKWebView *)component withJSON:(NSDictionary *)json withOptio
// user interaction enable/disable => disabled by default
component.userInteractionEnabled = NO;
-
if (json[@"action"]) {
// if there's an 'action' attribute, delegate the event handling to this component
@@ -140,7 +131,7 @@ + (UIView *)build:(WKWebView *)component withJSON:(NSDictionary *)json withOptio
}
+ (void)actionButtonClicked:(UIButton *)sender {
- DTLogDebug(@"sender.payload = %@", sender.payload);
+ DTLogDebug (@"sender.payload = %@", sender.payload);
if (sender.payload && sender.payload[@"action"]) {
[[Jason client] call:sender.payload[@"action"]];
diff --git a/xcode/Jasonette/Components/image/JasonImageComponent.m b/xcode/Jasonette/Components/image/JasonImageComponent.m
index 3b92292a..777ae84a 100644
--- a/xcode/Jasonette/Components/image/JasonImageComponent.m
+++ b/xcode/Jasonette/Components/image/JasonImageComponent.m
@@ -80,16 +80,16 @@ + (UIView *)build:(UIImageView *)component withJSON:(NSDictionary *)json withOpt
placeholderImage:placeholder_image
completed:^(UIImage * i, NSError * error, SDImageCacheType cacheType, NSURL * imageURL) {
if (!error) {
- JasonComponentFactory.imageLoaded[url] = [NSValue valueWithCGSize:i.size];
-
- if (style[@"color"]) {
- NSString * colorHex = style[@"color"];
- UIColor * c = [JasonHelper colorwithHexString:colorHex
- alpha:1.0];
- UIImage * image = [i imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
- [component setTintColor:c];
- [component setImage:image];
- }
+ JasonComponentFactory.imageLoaded[url] = [NSValue valueWithCGSize:i.size];
+
+ if (style[@"color"]) {
+ NSString * colorHex = style[@"color"];
+ UIColor * c = [JasonHelper colorwithHexString:colorHex
+ alpha:1.0];
+ UIImage * image = [i imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ [component setTintColor:c];
+ [component setImage:image];
+ }
}
}];
}
@@ -108,7 +108,7 @@ + (UIView *)build:(UIImageView *)component withJSON:(NSDictionary *)json withOpt
// don't do anything about the height, it will be handled in JasonComponent
} else {
if (!style[@"height"]) {
- // Width is set but height is not
+ // Width is set but height is not
CGFloat aspectRatioMult;
if (JasonComponentFactory.imageLoaded[url]) {
@@ -139,7 +139,7 @@ + (UIView *)build:(UIImageView *)component withJSON:(NSDictionary *)json withOpt
// don't do anything about the width, it will be handled in JasonComponent
} else {
if (!style[@"width"]) {
- // Height is set but width is not
+ // Height is set but width is not
CGFloat aspectRatioMult;
if (JasonComponentFactory.imageLoaded[url]) {
diff --git a/xcode/Jasonette/Components/map/JasonMapComponent.m b/xcode/Jasonette/Components/map/JasonMapComponent.m
index 8deb28ee..e354168b 100644
--- a/xcode/Jasonette/Components/map/JasonMapComponent.m
+++ b/xcode/Jasonette/Components/map/JasonMapComponent.m
@@ -16,6 +16,7 @@ + (UIView *)build:(MKMapView *)component withJSON:(NSDictionary *)json withOptio
// Map Style
NSDictionary * style = json[@"style"];
+
component.mapType = MKMapTypeStandard;
if (style && style[@"type"]) {
@@ -119,6 +120,7 @@ + (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id = 14.5
+ */
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface JasonATTrackingManager : NSObject
+
++ (void) showWithSettings: (NSDictionary *) settings;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Jasonette/Core/AppDelegate/ATTrackingManager/JasonATTrackingManager.m b/xcode/Jasonette/Core/AppDelegate/ATTrackingManager/JasonATTrackingManager.m
new file mode 100644
index 00000000..6d16f49e
--- /dev/null
+++ b/xcode/Jasonette/Core/AppDelegate/ATTrackingManager/JasonATTrackingManager.m
@@ -0,0 +1,33 @@
+//
+// JasonATTrackingManager.m
+// Jasonette
+//
+// Created by Jasonelle Team on 17-06-21.
+// Copyright © 2021 Jasonette. All rights reserved.
+//
+
+#import "JasonATTrackingManager.h"
+#import "JasonLogger.h"
+#import
+
+@implementation JasonATTrackingManager
+
++ (void) showWithSettings: (NSDictionary *) settings {
+
+ DTLogDebug(@"Checking ATTracking");
+
+ if (@available(iOS 14, *)) {
+ BOOL trackingEnabled = [settings[@"tracking_enabled"] boolValue];
+ if (trackingEnabled) {
+ [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
+ DTLogDebug (@"ATTracking status %d", status);
+ }];
+ } else {
+ DTLogDebug (@"ATTracking is disabled.");
+ }
+ } else {
+ DTLogDebug(@"iOS < 14.5 ATTracking not needed.");
+ }
+}
+
+@end
diff --git a/xcode/Jasonette/Core/AppDelegate/JasonAppDelegate.m b/xcode/Jasonette/Core/AppDelegate/JasonAppDelegate.m
index 20e6d75c..af41d452 100644
--- a/xcode/Jasonette/Core/AppDelegate/JasonAppDelegate.m
+++ b/xcode/Jasonette/Core/AppDelegate/JasonAppDelegate.m
@@ -7,6 +7,7 @@
#import "JasonAppDelegate.h"
#import "JasonLogger.h"
#import "JasonNSClassFromString.h"
+#import "JasonATTrackingManager.h"
static NSURL * _launchURL;
static NSArray * _services;
@@ -211,6 +212,9 @@ + (BOOL) application:(UIApplication *)application
DTLogInfo (@"Begin Building Screen");
[[Jason client] start:nil];
+
+ [JasonATTrackingManager showWithSettings:[[Jason client] getSettings]];
+
return YES;
}
@@ -317,20 +321,30 @@ + (void) application:(UIApplication *)application
+ (void) application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
- NSString * device_token = [[NSString alloc]initWithFormat:@"%@",
- [[[deviceToken description]
- stringByTrimmingCharactersInSet:[NSCharacterSet
- characterSetWithCharactersInString:@"<>"]]
- stringByReplacingOccurrencesOfString:@" "
- withString:@""]];
+ // Device token can be have variable length.
+ // Also using [deviceToken description] is not a good practice (description can change in the future)
+ // Solution based on this answer https://stackoverflow.com/a/16411517
- DTLogInfo (@"Got Device Token");
- DTLogDebug (@"Got Device Token %@", device_token);
+ const char * data = [deviceToken bytes];
+ NSMutableString * token = [@"" mutableCopy];
+ NSMutableString * tokenLowerCase = [@"" mutableCopy];
+
+ for (NSUInteger i = 0; i < deviceToken.length; i++) {
+ [token appendFormat:@"%02.2hhX", data[i]];
+ [tokenLowerCase appendFormat:@"%02.2hhx", data[i]];
+ }
+
+ NSDictionary * params = @{
+ @"token": token,
+ @"tokenlower": tokenLowerCase
+ };
+
+ DTLogDebug (@"Got Device Token %@ %@", params, deviceToken);
[[NSNotificationCenter defaultCenter]
postNotificationName:@"onRemoteNotificationDeviceRegistered"
object:nil
- userInfo:@{ @"token": device_token }];
+ userInfo:params];
}
+ (void) application:(UIApplication *)application
diff --git a/xcode/Jasonette/Core/Components/Factory/JasonComponentFactory.m b/xcode/Jasonette/Core/Components/Factory/JasonComponentFactory.m
index 77144619..2c5da6fc 100644
--- a/xcode/Jasonette/Core/Components/Factory/JasonComponentFactory.m
+++ b/xcode/Jasonette/Core/Components/Factory/JasonComponentFactory.m
@@ -102,6 +102,7 @@ + (NSMutableDictionary *)applyStylesheet:(NSDictionary *)json
}
NSMutableDictionary * stylizedComponent = [json mutableCopy];
+
stylizedComponent[@"style"] = styles;
DTLogInfo (@"End Stylesheet");
diff --git a/xcode/Jasonette/Core/Components/JasonComponent/JasonComponent.m b/xcode/Jasonette/Core/Components/JasonComponent/JasonComponent.m
index 68244266..f1298aef 100644
--- a/xcode/Jasonette/Core/Components/JasonComponent/JasonComponent.m
+++ b/xcode/Jasonette/Core/Components/JasonComponent/JasonComponent.m
@@ -220,11 +220,11 @@ + (void)stylize:(NSDictionary *)json
DTLogInfo (@"Applying Align %@", style[@"align"]);
NSDictionary * alignment_map = @{
- @"left": @(NSTextAlignmentLeft),
- @"center": @(NSTextAlignmentCenter),
- @"right": @(NSTextAlignmentRight),
- @"justified": @(NSTextAlignmentJustified),
- @"natural": @(NSTextAlignmentNatural)
+ @"left": @(NSTextAlignmentLeft),
+ @"center": @(NSTextAlignmentCenter),
+ @"right": @(NSTextAlignmentRight),
+ @"justified": @(NSTextAlignmentJustified),
+ @"natural": @(NSTextAlignmentNatural)
};
if ([el respondsToSelector:@selector(textAlignment)]) {
diff --git a/xcode/Jasonette/Core/Helpers/INTUAutoRemoveObserver/INTUAutoRemoveObserver.h b/xcode/Jasonette/Core/Helpers/INTUAutoRemoveObserver/INTUAutoRemoveObserver.h
old mode 100755
new mode 100644
index 8b5fb0c3..f98117cd
--- a/xcode/Jasonette/Core/Helpers/INTUAutoRemoveObserver/INTUAutoRemoveObserver.h
+++ b/xcode/Jasonette/Core/Helpers/INTUAutoRemoveObserver/INTUAutoRemoveObserver.h
@@ -86,8 +86,8 @@
@interface INTUAutoRemoveObserver : NSObject
// Notification Center observers
-+(void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender;
++ (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender;
-+(void)addObserver:(id)notificationObserver forName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification * note))block;
++ (void)addObserver:(id)notificationObserver forName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification * note))block;
@end
diff --git a/xcode/Jasonette/Core/Helpers/INTUAutoRemoveObserver/INTUAutoRemoveObserver.m b/xcode/Jasonette/Core/Helpers/INTUAutoRemoveObserver/INTUAutoRemoveObserver.m
old mode 100755
new mode 100644
index b45be5b1..28a8ba80
--- a/xcode/Jasonette/Core/Helpers/INTUAutoRemoveObserver/INTUAutoRemoveObserver.m
+++ b/xcode/Jasonette/Core/Helpers/INTUAutoRemoveObserver/INTUAutoRemoveObserver.m
@@ -34,7 +34,7 @@ @interface INTUAutoRemoveObserver ()
@property (nonatomic, unsafe_unretained) id notificationObserver;
@property (nonatomic, assign) id notificationSender;
-@property (nonatomic, copy) NSString* notificationName;
+@property (nonatomic, copy) NSString * notificationName;
// This is to store a reference to any block created observer
@property (nonatomic, strong) id blockObserver;
@@ -43,52 +43,51 @@ @interface INTUAutoRemoveObserver ()
@implementation INTUAutoRemoveObserver
-+(void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
++ (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
{
- // Create the remover object
- INTUAutoRemoveObserver* remover = [[INTUAutoRemoveObserver alloc] init];
- remover.notificationObserver = notificationObserver;
- remover.notificationName = notificationName;
- remover.notificationSender = notificationSender;
-
- // Keep this object around for the lifetime of the observer
- objc_setAssociatedObject(notificationObserver, (__bridge const void *)(remover), remover, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-
- // Now register for the notification
- [[NSNotificationCenter defaultCenter] addObserver:notificationObserver
- selector:notificationSelector
- name:notificationName
- object:notificationSender];
+ // Create the remover object
+ INTUAutoRemoveObserver * remover = [[INTUAutoRemoveObserver alloc] init];
+
+ remover.notificationObserver = notificationObserver;
+ remover.notificationName = notificationName;
+ remover.notificationSender = notificationSender;
+
+ // Keep this object around for the lifetime of the observer
+ objc_setAssociatedObject (notificationObserver, (__bridge const void *)(remover), remover, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ // Now register for the notification
+ [[NSNotificationCenter defaultCenter] addObserver:notificationObserver
+ selector:notificationSelector
+ name:notificationName
+ object:notificationSender];
}
-+(void)addObserver:(id)notificationObserver forName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *))block;
-{
- // Create the remover object
- INTUAutoRemoveObserver* remover = [[INTUAutoRemoveObserver alloc] init];
-
- id blockObserver = [[NSNotificationCenter defaultCenter] addObserverForName:name
- object:obj
- queue:queue
- usingBlock:block];
-
- // Keep this object around for the lifetime of the notificationObserver object
- objc_setAssociatedObject(notificationObserver, (__bridge const void *)(remover), remover, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-
- remover.blockObserver = blockObserver;
++ (void)addObserver:(id)notificationObserver forName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *))block; {
+ // Create the remover object
+ INTUAutoRemoveObserver * remover = [[INTUAutoRemoveObserver alloc] init];
+
+ id blockObserver = [[NSNotificationCenter defaultCenter] addObserverForName:name
+ object:obj
+ queue:queue
+ usingBlock:block];
+
+ // Keep this object around for the lifetime of the notificationObserver object
+ objc_setAssociatedObject (notificationObserver, (__bridge const void *)(remover), remover, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+
+ remover.blockObserver = blockObserver;
}
--(void)dealloc
+- (void)dealloc
{
- if ( self.blockObserver ) {
- // A block based notification center observer
- [[NSNotificationCenter defaultCenter] removeObserver:self.blockObserver];
- }
- else {
- // A selector based notification center observer
- [[NSNotificationCenter defaultCenter] removeObserver:self.notificationObserver
- name:self.notificationName
- object:self.notificationSender];
- }
+ if (self.blockObserver) {
+ // A block based notification center observer
+ [[NSNotificationCenter defaultCenter] removeObserver:self.blockObserver];
+ } else {
+ // A selector based notification center observer
+ [[NSNotificationCenter defaultCenter] removeObserver:self.notificationObserver
+ name:self.notificationName
+ object:self.notificationSender];
+ }
}
@end
diff --git a/xcode/Jasonette/Core/Helpers/JasonHelper/JasonHelper.h b/xcode/Jasonette/Core/Helpers/JasonHelper/JasonHelper.h
index 7a7f09b2..3cbc4642 100644
--- a/xcode/Jasonette/Core/Helpers/JasonHelper/JasonHelper.h
+++ b/xcode/Jasonette/Core/Helpers/JasonHelper/JasonHelper.h
@@ -10,6 +10,7 @@
#import "UICKeyChainStore.h"
#import "JasonParser.h"
@interface JasonHelper : NSObject
+ NS_ASSUME_NONNULL_BEGIN
+ (NSDate *)dateWithISO8601String:(NSString *)dateString;
+ (NSDate *)dateFromString:(NSString *)dateString withFormat:(NSString *)dateFormat;
+ (MFMessageComposeViewController *)sendSMS:(NSString *)message to:(NSString *)phone;
@@ -48,5 +49,6 @@
+ (id)getPlistSettings:(NSString *)key;
+ (id) loadErrorJson;
-+ (nullable NSDictionary *) hjson_to_json: (nonnull NSString *) content;
++ (nullable NSDictionary *)hjson_to_json:(nonnull NSString *)content;
+NS_ASSUME_NONNULL_END
@end
diff --git a/xcode/Jasonette/Core/Helpers/JasonHelper/JasonHelper.m b/xcode/Jasonette/Core/Helpers/JasonHelper/JasonHelper.m
index f774505f..a8f1af81 100644
--- a/xcode/Jasonette/Core/Helpers/JasonHelper/JasonHelper.m
+++ b/xcode/Jasonette/Core/Helpers/JasonHelper/JasonHelper.m
@@ -32,9 +32,11 @@ + (NSDate *)dateFromString:(NSString *)dateString
NSLocale * locale = [[NSLocale alloc]
initWithLocaleIdentifier:@"en_US_POSIX"];
+
[dateFormatter setLocale:locale];
NSDate * date = [dateFormatter dateFromString:dateString];
+
return date;
}
@@ -46,6 +48,7 @@ + (MFMessageComposeViewController *)sendSMS:(NSString *)message to:(NSString *)p
NSArray * recipents = @[phone];
MFMessageComposeViewController * messageController = [[MFMessageComposeViewController alloc] init];
+
[messageController setRecipients:recipents];
[messageController setBody:message];
[messageController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
@@ -183,13 +186,13 @@ + (UIColor *)colorwithHexString:(NSString *)hexStr alpha:(CGFloat)alpha
// Less common colors
NSDictionary * colors = @{
- @"red": [UIColor redColor],
- @"green": [UIColor greenColor],
- @"yellow": [UIColor yellowColor],
- @"blue": [UIColor blueColor],
- @"magenta": [UIColor magentaColor],
- @"gray": [UIColor grayColor],
- @"orange": [UIColor orangeColor]
+ @"red": [UIColor redColor],
+ @"green": [UIColor greenColor],
+ @"yellow": [UIColor yellowColor],
+ @"blue": [UIColor blueColor],
+ @"magenta": [UIColor magentaColor],
+ @"gray": [UIColor grayColor],
+ @"orange": [UIColor orangeColor]
};
UIColor * color = colors[colorName];
@@ -244,6 +247,7 @@ + (UIImage *)scaleImage:(UIImage *)image ToSize:(CGSize)newSize {
UIGraphicsBeginImageContextWithOptions (newSize, NO, 0);
[image drawInRect:scaledImageRect];
UIImage * scaledImage = UIGraphicsGetImageFromCurrentImageContext ();
+
UIGraphicsEndImageContext ();
return scaledImage;
@@ -327,6 +331,7 @@ + (UIImage *)colorize:(UIImage *)image into:(UIColor *)color {
UIGraphicsBeginImageContextWithOptions (image.size, YES, [[UIScreen mainScreen] scale]);
CGRect contextRect;
+
contextRect.origin.x = 0.0f;
contextRect.origin.y = 0.0f;
contextRect.size = [image size];
@@ -334,6 +339,7 @@ + (UIImage *)colorize:(UIImage *)image into:(UIColor *)color {
// Retrieve source image and begin image context
CGSize itemImageSize = [image size];
CGPoint itemImagePosition;
+
itemImagePosition.x = ceilf ((contextRect.size.width - itemImageSize.width) / 2);
itemImagePosition.y = ceilf ((contextRect.size.height - itemImageSize.height) );
@@ -364,6 +370,7 @@ + (UIImage *)colorize:(UIImage *)image into:(UIColor *)color {
CGContextEndTransparencyLayer (c);
UIImage * img = UIGraphicsGetImageFromCurrentImageContext ();
+
UIGraphicsEndImageContext ();
return img;
}
@@ -418,6 +425,7 @@ + (NSString *)stringify:(id)value
writerWithMaxDepth:0
humanReadable:NO
sortKeys:YES];
+
@try {
NSString * ret = [writer stringWithObject:value];
@@ -533,7 +541,8 @@ + (NSString *)prependProtocolToUrl:(NSString *)url {
+ (void)setStatusBarBackgroundColor:(UIColor *)color
{
- if (@available(iOS 13, *)) {} else {
+ if (@available(iOS 13, *)) {
+ } else {
UIView * statusBar = [[[UIApplication
sharedApplication]
valueForKey:@"statusBarWindow"]
@@ -588,6 +597,7 @@ + (BOOL)isURL:(NSURL *)url equivalentTo:(NSString *)urlString {
NSURLComponents * urlComponents2 = [[NSURLComponents alloc] initWithURL:[NSURL URLWithString:urlString] resolvingAgainstBaseURL:NO];
+
urlComponents2.query = nil; // Strip out query parameters.
return [urlComponents.string isEqualToString:urlComponents2.string];
}
@@ -772,6 +782,7 @@ + (UIImage *)takescreenshot
}
UIImage * image = UIGraphicsGetImageFromCurrentImageContext ();
+
UIGraphicsEndImageContext ();
return image;
}
@@ -1074,53 +1085,52 @@ + (id)loadErrorJson
return [JasonHelper read_local_json:@"file://error.json"];
}
-
-+ (nullable NSDictionary *) hjson_to_json: (nonnull NSString *) content {
-
++ (nullable NSDictionary *)hjson_to_json:(nonnull NSString *)content {
NSStringEncoding encoding;
NSError * error = nil;
-
+
JSContext * context = [JSContext new];
+
[context setExceptionHandler:^(JSContext * context, JSValue * value) {
- DTLogWarning (@"%@", value);
- }];
-
+ DTLogWarning (@"%@", value);
+ }];
+
[context evaluateScript:@"var console = {};"];
context[@"console"][@"log"] = ^(NSString * message) {
DTLogDebug (@"JS: %@", message);
};
-
+
NSString * renderfile = [[NSBundle mainBundle] pathForResource:@"hjson" ofType:@"js"];
NSString * renderjs = [NSString stringWithContentsOfFile:renderfile
usedEncoding:&encoding
error:&error];
-
- DTLogDebug(@"Loading hjson.js");
+
+ DTLogDebug (@"Loading hjson.js");
[context evaluateScript:renderjs];
-
+
JSValue * render = context[@"to_json"];
JSValue * val = [render callWithArguments:@[content]];
NSString * json = [val toString];
-
- DTLogDebug(@"%@", json);
-
+
+ DTLogDebug (@"%@", json);
+
error = nil;
-
+
NSDictionary * result = [NSJSONSerialization
- JSONObjectWithData:[json
- dataUsingEncoding:NSUTF8StringEncoding]
- options:kNilOptions
- error:&error];
-
-
-
-
- if(error) {
- DTLogWarning(@"%@", error);
+ JSONObjectWithData:[json
+ dataUsingEncoding:NSUTF8StringEncoding]
+ options:kNilOptions
+ error:&error];
+
+
+
+
+ if (error) {
+ DTLogWarning (@"%@", error);
return nil;
}
-
+
return result;
}
@@ -1150,12 +1160,13 @@ + (id)read_local_json:(NSString *)url
if (error) {
NSString * content = [JasonHelper read_local_file:json];
- if(content) {
- DTLogDebug(@"Trying hjson.js");
+
+ if (content) {
+ DTLogDebug (@"Trying hjson.js");
result = [JasonHelper hjson_to_json:content];
}
-
- if(!result) {
+
+ if (!result) {
result = [JasonHelper loadErrorJson];
}
}
@@ -1172,6 +1183,7 @@ + (NSString *)normalized_url:(NSString *)url forOptions:(id)options {
normalized_url = [normalized_url stringByAppendingString:[NSString stringWithFormat:@"|%@", options]];
NSRegularExpression * regex = [NSRegularExpression regularExpressionWithPattern:@"[/:]" options:NSRegularExpressionCaseInsensitive error:nil];
+
normalized_url = [regex stringByReplacingMatchesInString:normalized_url options:0 range:NSMakeRange (0, [normalized_url length]) withTemplate:@"_"];
normalized_url = [[normalized_url componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] componentsJoinedByString:@""];
return normalized_url;
diff --git a/xcode/Jasonette/Core/Jason/Jason.h b/xcode/Jasonette/Core/Jason/Jason.h
index 133125ca..9f13d010 100644
--- a/xcode/Jasonette/Core/Jason/Jason.h
+++ b/xcode/Jasonette/Core/Jason/Jason.h
@@ -49,6 +49,8 @@
- (Jason *) attach:(JasonViewController *)viewController;
- (Jason *)detach:(JasonViewController *)viewController;
+- (NSDictionary *) getSettings;
+
- (void) cancel;
- (void) ok;
- (void)ok:(NSDictionary *)result;
diff --git a/xcode/Jasonette/Core/Jason/Jason.m b/xcode/Jasonette/Core/Jason/Jason.m
index 739002ef..d87f6c34 100644
--- a/xcode/Jasonette/Core/Jason/Jason.m
+++ b/xcode/Jasonette/Core/Jason/Jason.m
@@ -147,20 +147,20 @@ - (void)loadViewByFile:(NSString *)url
[self
include:jsonResponseObject
andCompletionHandler:^(id res)
- {
- id result = @{};
-
- if (res[@"$jason"]) {
- result = res[@"$jason"];
- } else {
- result = [JasonHelper loadErrorJson][@"$jason"];
- DTLogError (@"Missing $jason property in \n%@\n\n", res);
- }
-
- self->VC.original = @{ @"$jason": result };
- [self drawViewFromJason:self->VC.original
- asFinal:final];
- }];
+ {
+ id result = @{};
+
+ if (res[@"$jason"]) {
+ result = res[@"$jason"];
+ } else {
+ result = [JasonHelper loadErrorJson][@"$jason"];
+ DTLogError (@"Missing $jason property in \n%@\n\n", res);
+ }
+
+ self->VC.original = @{ @"$jason": result };
+ [self drawViewFromJason:self->VC.original
+ asFinal:final];
+ }];
}
#pragma mark - Jason Core API (USE ONLY THESE METHODS TO ACCESS Jason Core!)
@@ -179,39 +179,36 @@ - (void)start:(NSDictionary *)href
JasonAppDelegate * app = (JasonAppDelegate *)[[UIApplication sharedApplication] delegate];
NSDictionary * plist = [self getSettings];
BOOL appendNonce = [plist[@"append_nonce_to_url"] boolValue];
-
- DTLogDebug((appendNonce ? @"Nonce Activated" : @"Nonce Inactive"));
-
+
+ DTLogDebug ((appendNonce ? @"Nonce Activated" : @"Nonce Inactive"));
+
ROOT_URL = plist[@"url"];
NSURLComponents * components = [[NSURLComponents alloc]
initWithURL:[NSURL URLWithString:ROOT_URL]
- resolvingAgainstBaseURL:NO];
-
- if(appendNonce && components) {
-
- if(![components.scheme isEqualToString:@"file"]) {
-
- DTLogDebug(@"Adding Nonce");
-
+ resolvingAgainstBaseURL :NO];
+
+ if (appendNonce && components) {
+ if (![components.scheme isEqualToString:@"file"]) {
+ DTLogDebug (@"Adding Nonce");
+
NSURLQueryItem * nonce = [[NSURLQueryItem alloc]
initWithName:@"jasonnonce" value:[NSString
stringWithFormat:@"%u%f",
- arc4random_uniform(10000) + 1,
- CFAbsoluteTimeGetCurrent()]];
-
+ arc4random_uniform (10000) + 1,
+ CFAbsoluteTimeGetCurrent ()]];
+
NSMutableArray * items = [NSMutableArray
arrayWithCapacity:[components.queryItems count] + 1];
[items addObject:nonce];
-
- [components setQueryItems: items];
-
+
+ [components setQueryItems:items];
+
ROOT_URL = [components.URL absoluteString];
-
} else {
- DTLogDebug(@"file:// scheme used. nonce not added.");
+ DTLogDebug (@"file:// scheme used. nonce not added.");
}
}
-
+
DTLogDebug (@"Root Url %@", ROOT_URL);
if (!ROOT_URL || [ROOT_URL isEqualToString:@""] || !components) {
@@ -260,7 +257,7 @@ - (void)start:(NSDictionary *)href
}
if (href[@"loading"]) {
- vc.loading = href[@"loading"];
+ vc.loading = [href[@"loading"] boolValue];
DTLogDebug (@"Is Loading? %d", vc.loading);
}
}
@@ -454,11 +451,11 @@ - (void)loading:(BOOL)turnon {
if (turnon) {
[JDStatusBarNotification addStyleNamed:@"SBStyle1"
prepare:^JDStatusBarStyle *(JDStatusBarStyle * style) {
- style.barColor = self->navigationController.navigationBar.backgroundColor;
- style.textColor = self->navigationController.navigationBar.tintColor;
- style.animationType = JDStatusBarAnimationTypeFade;
- return style;
- }];
+ style.barColor = self->navigationController.navigationBar.backgroundColor;
+ style.textColor = self->navigationController.navigationBar.tintColor;
+ style.animationType = JDStatusBarAnimationTypeFade;
+ return style;
+ }];
[JDStatusBarNotification showWithStatus:@"Loading" styleName:@"SBStyle1"];
if (navigationController.navigationBar.barStyle == UIStatusBarStyleDefault) {
@@ -505,6 +502,7 @@ - (void)href {
VC.callback = memory._stack;
NSDictionary * href = [self options];
+
memory._stack = @{}; // empty stack before visiting
[self go:href];
}
@@ -656,10 +654,10 @@ - (void)menu {
[self->menu_component close];
if (item_action) {
- [memory set_stack:item_action];
- [self exec];
+ [memory set_stack:item_action];
+ [self exec];
} else if (item_href) {
- [self go:item_href];
+ [self go:item_href];
}
}];
@@ -861,7 +859,7 @@ - (void)lambda {
args = [self filloutTemplate:args withData:memory._register];
memory._register = @{ @"$jason": args };
} else {
- // do nothing. keep the register and propgate
+ // do nothing. keep the register and propgate
}
id lambda = [[VC valueForKey:@"events"] valueForKey:name];
@@ -1159,6 +1157,64 @@ - (void)scroll {
# pragma mark - View initialization & teardown
+- (NSDictionary *) getMethodForUrl: (NSString *) url {
+ // Check for [POST|GET|PUT] method in Url
+ NSString * pattern = @"\\[(POST|GET|PUT|HEAD|DELETE)\\]";
+ NSError * regexError = nil;
+
+ NSRegularExpression * regex = [NSRegularExpression
+ regularExpressionWithPattern:pattern
+ options:NSRegularExpressionCaseInsensitive | NSRegularExpressionDotMatchesLineSeparators
+ error:®exError];
+
+ if (regexError) {
+ DTLogWarning (@"Regex Error in Extracting URL pattern %@ error %@", pattern, regexError);
+ }
+
+ NSArray * matches = [regex matchesInString:url
+ options:NSMatchingWithoutAnchoringBounds
+ range:NSMakeRange (0, url.length)];
+
+
+ NSString * parsed = url;
+ NSString * method = @"get";
+
+
+ NSTextCheckingResult * match = [matches firstObject];
+ NSRange range;
+
+ if(match) {
+ range = [match rangeAtIndex:0];
+
+ if(range.length > 0) {
+ parsed = [url substringWithRange:NSMakeRange(0, url.length - range.length)];
+
+ method = [[[url substringWithRange:range]
+ lowercaseString]
+ stringByTrimmingCharactersInSet:[NSCharacterSet
+ characterSetWithCharactersInString:@"[]"]];
+ }
+
+ }
+
+ BOOL shouldDownload = YES;
+ // if the url contains brackets [] and its the same as the original
+ // it means it maybe has wrong format
+ // so its better to not download it or it could crash the app
+ if([parsed isEqualToString:url] && ([url containsString:@"["] || [url containsString:@"]"])) {
+ DTLogDebug(@"Mixin Parsed: %@ is equal to original url %@. Maybe is syntax error. Omitting download.", parsed, url);
+ parsed = @"";
+ shouldDownload = NO;
+ }
+
+ return @{
+ @"original": url,
+ @"parsed": parsed,
+ @"method": method,
+ @"shouldDownload": @(shouldDownload)
+ };
+}
+
- (void) include:(id)json
andCompletionHandler:(void (^)(id obj))callback
{
@@ -1175,6 +1231,9 @@ - (void) include:(id)json
NSString * pattern = @"\"([+@])\"[ ]*:[ ]*\"([^$\"@]+@)?([^$\"]+)\"";
NSMutableSet * urlSet = [NSMutableSet new];
+
+ NSMutableDictionary * parsedMixinItems = [@{} mutableCopy];
+
NSRegularExpression * regex = [NSRegularExpression
regularExpressionWithPattern:pattern
options:NSRegularExpressionCaseInsensitive | NSRegularExpressionDotMatchesLineSeparators
@@ -1208,10 +1267,18 @@ - (void) include:(id)json
if (group3.length > 0) {
// Group 3 is for the URL
NSString * url = [j substringWithRange:group3];
-
- if (!VC.requires[url]) {
- DTLogDebug (@"Adding object to url set %@", url);
- [urlSet addObject:url];
+
+ NSDictionary * params = [self getMethodForUrl:url];
+
+ DTLogDebug(@"Mixin Params: %@", params);
+
+ if([params[@"shouldDownload"] boolValue]) {
+ if (!VC.requires[params[@"parsed"]]) {
+ DTLogDebug (@"Adding Mixin URL %@", url);
+ parsedMixinItems[params[@"parsed"]] = params;
+ parsedMixinItems[url] = params;
+ [urlSet addObject:params[@"parsed"]];
+ }
}
}
}
@@ -1307,21 +1374,52 @@ - (void) include:(id)json
// 6. Start request
#pragma message "Start Request in Include"
- [manager GET:url
- parameters:parameters
- headers:nil
- progress:^(NSProgress * _Nonnull downloadProgress) { }
- success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
- {
- self->VC.requires[url] = responseObject;
- dispatch_group_leave (requireGroup);
+
+ DTLogDebug(@"Mixin Fetching %@ With Params %@", url, parsedMixinItems[url]);
+
+ NSString * method = parsedMixinItems[url][@"method"];
+ NSString * originalUrl = parsedMixinItems[url][@"original"];
+
+ // TODO: Maybe add a better handler and more http methods and params
+ if([method isEqualToString:@"post"]) {
+
+ DTLogDebug(@"Fetching with POST");
+
+ [manager POST:url
+ parameters:parameters
+ headers:nil
+ progress:^(NSProgress * _Nonnull downloadProgress) { }
+ success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
+ {
+ self->VC.requires[originalUrl] = responseObject;
+ DTLogDebug(@"VC.requires %@", self->VC.requires);
+ dispatch_group_leave (requireGroup);
+ }
+ failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
+ {
+ DTLogWarning (@"Error Fetching JSON From Url %@ %@", url, error);
+ self->VC.requires[originalUrl] = @{};
+ dispatch_group_leave (requireGroup);
+ }];
+ } else {
+ DTLogDebug(@"Fetching with GET");
+ [manager GET:url
+ parameters:parameters
+ headers:nil
+ progress:^(NSProgress * _Nonnull downloadProgress) { }
+ success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
+ {
+ self->VC.requires[originalUrl] = responseObject;
+ DTLogDebug(@"VC.requires %@", self->VC.requires);
+ dispatch_group_leave (requireGroup);
+ }
+ failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
+ {
+ DTLogWarning (@"Error Fetching JSON From Url %@ %@", url, error);
+ self->VC.requires[originalUrl] = @{};
+ dispatch_group_leave (requireGroup);
+ }];
}
- failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
- {
- DTLogWarning (@"Error Fetching JSON From Url %@ %@", url, error);
- self->VC.requires[url] = @{};
- dispatch_group_leave (requireGroup);
- }];
}
}
@@ -1341,10 +1439,12 @@ - (void) include:(id)json
callback (resolved);
}
+#pragma mark - $require action
- (void)require
{
DTLogInfo (@"Require Json");
NSString * origin_url = VC.url;
+
DTLogDebug (@"%@", origin_url);
/*
@@ -1384,11 +1484,11 @@ - (void)require
dispatch_group_enter (requireGroup);
if ([url containsString:@"file://"]) {
- // local
+ // local
return_value[url] = [JasonHelper read_local_json:url];
dispatch_group_leave (requireGroup);
} else {
- // 3. Setup networking
+ // 3. Setup networking
AFHTTPSessionManager * manager = [JasonNetworking manager];
AFJSONResponseSerializer * jsonResponseSerializer = [JasonNetworking serializer];
@@ -1441,15 +1541,15 @@ - (void)require
headers:nil
progress:^(NSProgress * _Nonnull downloadProgress) { }
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
- {
- return_value[url] = responseObject;
- dispatch_group_leave (requireGroup);
- }
+ {
+ return_value[url] = responseObject;
+ dispatch_group_leave (requireGroup);
+ }
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
- {
- DTLogError (@"Failed Requiring %@ %@", url, error);
- dispatch_group_leave (requireGroup);
- }];
+ {
+ DTLogError (@"Failed Requiring %@ %@", url, error);
+ dispatch_group_leave (requireGroup);
+ }];
}
}
}
@@ -1486,11 +1586,14 @@ - (void)require
});
}
+#pragma mark - Mixin
- (id)resolve_remote_reference:(NSString *)json
{
- DTLogDebug (@"Resolving Remote References");
+ DTLogInfo (@"Remote Reference Mixin");
+ DTLogDebug (@"Resolving Remote References For Json %@", json);
+
NSError * error;
-
+ // Mixins can be used with '@' or '+' symbol
// Remote url with path - convert "@": "blah.blah@https://www.google.com" to "{{#include $root[\"https://www.google.com\"].blah.blah}}": {}
// The pattern leaves out the pattern where it starts with "$" because that's a $document and will be resolved by resolve_local_reference
NSString * remote_pattern_with_path = @"\"([+@])\"[ ]*:[ ]*\"(([^$\"@]+)(@))([^\"]+)\"";
@@ -1532,7 +1635,8 @@ - (id)resolve_remote_reference:(NSString *)json
- (id)resolve_local_reference:(NSString *)json
{
- DTLogInfo (@"Resolving Local References");
+ DTLogInfo (@"Local Reference Mixin");
+ DTLogDebug (@"Resolving Local References For Json %@", json);
// Local - convert "@": "$document.blah.blah" to "{{#include $root.$document.blah.blah}}": {}
NSError * error;
@@ -1547,19 +1651,27 @@ - (id)resolve_local_reference:(NSString *)json
options:kNilOptions
range:NSMakeRange (0, json.length)
withTemplate:@"\"{{#include \\$root.$1}}\": {}"];
-
+
+
id tpl = [JasonHelper objectify:converted];
NSMutableDictionary * refs = [VC.requires mutableCopy];
+
refs[@"$document"] = VC.original;
+
+ DTLogDebug(@"TPL %@ Refs", tpl, refs);
id include_resolved = [JasonParser parse:refs with:tpl];
+
VC.original = include_resolved;
+
+ DTLogDebug(@"Resolved References %@", include_resolved);
return include_resolved;
}
+#pragma mark - Lifecycle
- (Jason *)detach:(JasonViewController *)viewController
{
// Need to clean up before leaving the view
@@ -1609,12 +1721,12 @@ - (Jason *)detach:(JasonViewController *)viewController
DTLogDebug (@"$webcontainer found");
if (VC.isMovingFromParentViewController || VC.isBeingDismissed) {
- // Web container AND coming back from the child view therefore it's ok to kill the child view's web container agent
+ // Web container AND coming back from the child view therefore it's ok to kill the child view's web container agent
DTLogDebug (@"Web container AND coming back from the child view therefore it's ok to kill the child view's web container agent");
[agent clear:key forVC:VC];
} else {
- // Otherwise it could be:
- // 1. Going from view A to view B (Don't kill view A's agent)
+ // Otherwise it could be:
+ // 1. Going from view A to view B (Don't kill view A's agent)
DTLogDebug (@"Going from view A to view B (Don't kill view A's agent)");
DTLogDebug (@"Not clearing $webcontainer");
}
@@ -1690,7 +1802,7 @@ - (Jason *)attach:(JasonViewController *)viewController {
header_needs_refresh = YES;
if (VC.rendered[@"nav"]) {
- // Deprecated
+ // Deprecated
[self setupHeader:VC.rendered[@"nav"]];
} else if (VC.rendered[@"header"]) {
[self setupHeader:VC.rendered[@"header"]];
@@ -1734,16 +1846,16 @@ - (Jason *)attach:(JasonViewController *)viewController {
*
********************************************************************************************************/
if (VC.rendered && rendered_page) {
- /*********************************************************************************************************
- *
- * We need to redraw the already rendered final result instead of the pre-rendered markup
- *
- * For example, in: {"head": {"title": "{{$params.name}}", ...}}
- * It will draw "{{$params.name}}" as the title of the nav bar if we don't draw from the already rendered markup.
- *
- ********************************************************************************************************/
+ /*********************************************************************************************************
+ *
+ * We need to redraw the already rendered final result instead of the pre-rendered markup
+ *
+ * For example, in: {"head": {"title": "{{$params.name}}", ...}}
+ * It will draw "{{$params.name}}" as the title of the nav bar if we don't draw from the already rendered markup.
+ *
+ ********************************************************************************************************/
if (VC.rendered[@"nav"]) {
- // Deprecated
+ // Deprecated
[self setupHeader:VC.rendered[@"nav"]];
} else if (VC.rendered[@"header"]) {
[self setupHeader:VC.rendered[@"header"]];
@@ -1761,11 +1873,11 @@ - (Jason *)attach:(JasonViewController *)viewController {
if (VC.rendered[@"footer"] && VC.rendered[@"footer"][@"tabs"]) {
if (![old_tabs isEqualToDictionary:VC.rendered[@"footer"][@"tabs"]]) {
- // Use this
+ // Use this
[self setupTabBar:VC.rendered[@"footer"][@"tabs"]];
}
} else {
- // Deprecated
+ // Deprecated
if (![old_tabs isEqualToDictionary:VC.rendered[@"tabs"]]) {
[self setupTabBar:VC.rendered[@"tabs"]];
}
@@ -1798,12 +1910,12 @@ - (Jason *)attach:(JasonViewController *)viewController {
}
}
- // set "rendered_page" to VC.rendered for cases when we're coming back from another view
- // so that rendered_page will be always in sync even when there is no $show handler to refresh the view.
+ // set "rendered_page" to VC.rendered for cases when we're coming back from another view
+ // so that rendered_page will be always in sync even when there is no $show handler to refresh the view.
rendered_page = VC.rendered;
- // if the view gets updated inside onShow, the rendered_page will update automatically
+ // if the view gets updated inside onShow, the rendered_page will update automatically
[self onShow];
}
/*********************************************************************************************************
@@ -1812,25 +1924,25 @@ - (Jason *)attach:(JasonViewController *)viewController {
*
********************************************************************************************************/
else {
- /*********************************************************************************************************
- *
- * If content has been loaded already,
- * 1. redraw navbar
- * 2. re-setup event listener
- *
- ********************************************************************************************************/
+ /*********************************************************************************************************
+ *
+ * If content has been loaded already,
+ * 1. redraw navbar
+ * 2. re-setup event listener
+ *
+ ********************************************************************************************************/
if (VC.contentLoaded) {
- // If content already loaded,
- // 1. just setup the navbar so the navbar will have the correct style
- // 2. trigger load events ($show or $load)
+ // If content already loaded,
+ // 1. just setup the navbar so the navbar will have the correct style
+ // 2. trigger load events ($show or $load)
[self setupHeader:VC.nav];
[self onShow];
}
- /*********************************************************************************************************
- *
- * If content has not been loaded yet, do a fresh reload
- *
- ********************************************************************************************************/
+ /*********************************************************************************************************
+ *
+ * If content has not been loaded yet, do a fresh reload
+ *
+ ********************************************************************************************************/
else {
if (VC.preload && VC.preload[@"style"] && VC.preload[@"style"][@"background"]) {
if ([VC.preload[@"style"][@"background"] isKindOfClass:[NSDictionary class]]) {
@@ -1979,14 +2091,25 @@ - (NSDictionary *)getEnv {
NSDictionary * info = [[NSBundle mainBundle] infoDictionary];
NSString * version = [info objectForKey:@"CFBundleShortVersionString"];
NSString * build = [info objectForKey:(NSString *)kCFBundleVersionKey];
+
dict[@"app"] = @{ @"build": build, @"version": version };
CGRect bounds = [[UIScreen mainScreen] bounds];
+
dict[@"device"] = @{
- @"width": @(bounds.size.width),
- @"height": @(bounds.size.height),
- @"os": @{ @"name": @"ios", @"version": [[UIDevice currentDevice] systemVersion] },
- @"language": [[NSLocale preferredLanguages] objectAtIndex:0]
+ @"uuid": [[[UIDevice currentDevice] identifierForVendor] UUIDString],
+ @"width": @(bounds.size.width),
+ @"height": @(bounds.size.height),
+ @"os": @{
+ @"name": [[[UIDevice currentDevice] systemName] lowercaseString],
+ @"version": [[UIDevice currentDevice] systemVersion]
+ },
+ @"name": [[UIDevice currentDevice] name],
+ @"model": @{
+ @"name": [[UIDevice currentDevice] model],
+ @"localized": [[UIDevice currentDevice] localizedModel]
+ },
+ @"language": [[NSLocale preferredLanguages] objectAtIndex:0]
};
NSURLComponents * components = [NSURLComponents componentsWithString:self->VC.url];
@@ -1999,8 +2122,8 @@ - (NSDictionary *)getEnv {
}
dict[@"view"] = @{
- @"url": self->VC.url,
- @"params": params
+ @"url": self->VC.url,
+ @"params": params
};
return dict;
@@ -2055,6 +2178,7 @@ - (NSDictionary *)variables {
}
NSDictionary * env = [self getEnv];
+
data_stub[@"$env"] = env;
if (self->VC.current_cache) {
@@ -2226,7 +2350,7 @@ - (void)reload
* Handling data uri
***************************************************/
if ([self->VC.url hasPrefix:@"data:application/json"]) {
- // if data uri, parse it into NSData
+ // if data uri, parse it into NSData
NSURL * url = [NSURL URLWithString:self->VC.url];
NSData * jsonData = [NSData dataWithContentsOfURL:url];
NSError * error;
@@ -2251,7 +2375,7 @@ - (void)reload
AFJSONResponseSerializer * jsonResponseSerializer = [JasonNetworking serializer];
NSMutableSet * jsonAcceptableContentTypes = [NSMutableSet setWithSet:jsonResponseSerializer.acceptableContentTypes];
- // Assumes that content type is json, even the text/plain ones (Some hosting sites respond with data_type of text/plain even when it's actually a json, so we accept even text/plain as json by default)
+ // Assumes that content type is json, even the text/plain ones (Some hosting sites respond with data_type of text/plain even when it's actually a json, so we accept even text/plain as json by default)
[jsonAcceptableContentTypes addObject:@"text/plain"];
[jsonAcceptableContentTypes addObject:@"text/html"];
@@ -2276,40 +2400,40 @@ - (void)reload
progress:^(NSProgress * _Nonnull downloadProgress) { }
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// Ignore if the url is different
- if (![JasonHelper isURL:task.originalRequest.URL
- equivalentTo :self->VC.url]) {
- return;
+ if (![JasonHelper isURL:task.originalRequest.URL
+ equivalentTo :self->VC.url]) {
+ return;
}
self->VC.original = responseObject;
[self include:responseObject
- andCompletionHandler:^(id res)
- {
- dispatch_async (dispatch_get_main_queue (), ^{
- self->VC.contentLoaded = NO;
-
- self->VC.original = @{ @"$jason": res[@"$jason"] };
- [self drawViewFromJason:self->VC.original
- asFinal:YES];
- });
+ andCompletionHandler:^(id res)
+ {
+ dispatch_async (dispatch_get_main_queue (), ^{
+ self->VC.contentLoaded = NO;
+
+ self->VC.original = @{ @"$jason": res[@"$jason"] };
+ [self drawViewFromJason:self->VC.original
+ asFinal:YES];
+ });
}];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
- {
- if (!self->VC.offline) {
- DTLogWarning (@"%@", error);
- [[Jason client] loadViewByFile:@"file://error.json"
- asFinal:YES];
- [[Jason client] call:@{
- @"type": @"$util.alert",
- @"options": @{
- @"title": @"Debug",
- @"description": [error localizedDescription]
- }
- }];
- }
- }];
+ {
+ if (!self->VC.offline) {
+ DTLogWarning (@"%@", error);
+ [[Jason client] loadViewByFile:@"file://error.json"
+ asFinal:YES];
+ [[Jason client] call:@{
+ @"type": @"$util.alert",
+ @"options": @{
+ @"title": @"Debug",
+ @"description": [error localizedDescription]
+ }
+ }];
+ }
+ }];
}
}
}
@@ -2353,28 +2477,28 @@ - (void)drawViewFromJason:(NSDictionary *)jason
if (body) {
if (body[@"nav"]) {
- // Deprecated
+ // Deprecated
[self setupHeader:body[@"nav"]];
} else if (body[@"header"]) {
- // Use this
+ // Use this
[self setupHeader:body[@"header"]];
} else {
[self setupHeader:nil];
}
if (body[@"footer"] && body[@"footer"][@"tabs"]) {
- // Use this
+ // Use this
[self setupTabBar:body[@"footer"][@"tabs"]];
} else {
- // Deprecated
+ // Deprecated
[self setupTabBar:body[@"tabs"]];
}
- // By default, "body" is the markup that will be rendered
+ // By default, "body" is the markup that will be rendered
rendered_page = dom[@"body"];
} else {
- // Don't remove the header and footer even if it doesn't exist yet
- // and let it be overridden in $render
+ // Don't remove the header and footer even if it doesn't exist yet
+ // and let it be overridden in $render
}
/****************************************************************************
@@ -2387,7 +2511,7 @@ - (void)drawViewFromJason:(NSDictionary *)jason
NSDictionary * body_parser = VC.parser[@"body"];
if (body_parser) {
- // parse the data with the template to dynamically build the view
+ // parse the data with the template to dynamically build the view
NSMutableDictionary * data_stub;
if (VC.data) {
@@ -2409,21 +2533,21 @@ - (void)drawViewFromJason:(NSDictionary *)jason
if (rendered_page[@"style"] && rendered_page[@"style"][@"background"]) {
if ([rendered_page[@"style"][@"background"] isKindOfClass:[NSDictionary class]]) {
- // Advanced background
- // example:
- // "background": {
- // "type": "camera",
- // "options": {
- // ...
- // }
- // }
+ // Advanced background
+ // example:
+ // "background": {
+ // "type": "camera",
+ // "options": {
+ // ...
+ // }
+ // }
[self drawAdvancedBackground:rendered_page[@"style"][@"background"]];
} else {
[self drawBackground:rendered_page[@"style"][@"background"]];
}
} else if (rendered_page[@"background"]) {
if ([rendered_page[@"background"] isKindOfClass:[NSDictionary class]]) {
- // Advanced background
+ // Advanced background
DTLogDebug (@"Detected Advanced Background");
[self drawAdvancedBackground:rendered_page[@"background"]];
} else {
@@ -2541,6 +2665,10 @@ - (void)drawAdvancedBackground:(NSDictionary *)bg forVC:(JasonViewController *)v
payload[@"com.jasonelle.state:stop-reloading"] = @YES;
}
+ if (bg[@"options"]) {
+ payload[@"options"] = bg[@"options"];
+ }
+
DTLogDebug (@"Loading Background with Payload %@", payload);
#pragma message "JasonAgentService Setup"
@@ -2807,7 +2935,7 @@ - (void)setupHead:(NSDictionary *)head {
// 3. agents
if (!VC.agentReady) {
- // Agents must be setup ONLY once, AFTER the true view has finished loading.
+ // Agents must be setup ONLY once, AFTER the true view has finished loading.
if (head[@"agents"] && [head[@"agents"] isKindOfClass:[NSDictionary class]] && [head[@"agents"] count] > 0) {
for (NSString * key in head[@"agents"]) {
JasonAgentService * agent = self.services[@"JasonAgentService"];
@@ -2851,7 +2979,7 @@ - (void)setupHeader:(NSDictionary *)nav forVC:(JasonViewController *)v
if (v.old_header && [[v.old_header description] isEqualToString:[nav description]]) {
// if the header is the same as the value trying to set,
if (rendered_page[@"header"] && [[rendered_page[@"header"] description] isEqualToString:[v.old_header description]]) {
- // and if the currently visible rendered_page's header is the same as the VC's old_header, ignore.
+ // and if the currently visible rendered_page's header is the same as the VC's old_header, ignore.
return;
}
}
@@ -2897,10 +3025,10 @@ - (void)setupHeader:(NSDictionary *)nav forVC:(JasonViewController *)v
navigationController.navigationBar.barTintColor = background;
navigationController.navigationBar.tintColor = color;
navigationController.navigationBar.titleTextAttributes = @{
- NSForegroundColorAttributeName: color,
- NSFontAttributeName: [UIFont
- fontWithName:@"HelveticaNeue-CondensedBold"
- size:18.0]
+ NSForegroundColorAttributeName: color,
+ NSFontAttributeName: [UIFont
+ fontWithName:@"HelveticaNeue-CondensedBold"
+ size:18.0]
};
return;
}
@@ -2908,10 +3036,10 @@ - (void)setupHeader:(NSDictionary *)nav forVC:(JasonViewController *)v
navigationController.navigationBar.barStyle = UIStatusBarStyleLightContent;
navigationController.navigationBar.titleTextAttributes = @{
- NSForegroundColorAttributeName: color,
- NSFontAttributeName: [UIFont
- fontWithName:@"HelveticaNeue-CondensedBold"
- size:18.0]
+ NSForegroundColorAttributeName: color,
+ NSFontAttributeName: [UIFont
+ fontWithName:@"HelveticaNeue-CondensedBold"
+ size:18.0]
};
navigationController.navigationBar.hidden = NO;
@@ -2974,10 +3102,10 @@ - (void)setupHeader:(NSDictionary *)nav forVC:(JasonViewController *)v
}
navigationController.navigationBar.titleTextAttributes = @{
- NSForegroundColorAttributeName: color,
- NSFontAttributeName: [UIFont
- fontWithName:font_name
- size:[font_size integerValue]]
+ NSForegroundColorAttributeName: color,
+ NSFontAttributeName: [UIFont
+ fontWithName:font_name
+ size:[font_size integerValue]]
};
} else {
navigationController.navigationBar.barStyle = UIStatusBarStyleLightContent;
@@ -2985,10 +3113,10 @@ - (void)setupHeader:(NSDictionary *)nav forVC:(JasonViewController *)v
NSString * font_name = @"HelveticaNeue-CondensedBold";
NSString * font_size = @"18";
navigationController.navigationBar.titleTextAttributes = @{
- NSForegroundColorAttributeName: color,
- NSFontAttributeName: [UIFont
- fontWithName:font_name
- size:[font_size integerValue]]
+ NSForegroundColorAttributeName: color,
+ NSFontAttributeName: [UIFont
+ fontWithName:font_name
+ size:[font_size integerValue]]
};
}
@@ -3072,11 +3200,11 @@ - (void)setupHeader:(NSDictionary *)nav forVC:(JasonViewController *)v
}
completed:^(UIImage * image, NSError * error, SDImageCacheType cacheType, BOOL finished, NSURL * imageURL) {
if (image) {
- dispatch_async (dispatch_get_main_queue (), ^{
- [self setMenuButtonImage:image
- forButton:btn
- withMenu:left_menu]; //
- });
+ dispatch_async (dispatch_get_main_queue (), ^{
+ [self setMenuButtonImage:image
+ forButton:btn
+ withMenu:left_menu]; //
+ });
}
}];
}
@@ -3145,11 +3273,11 @@ - (void)setupHeader:(NSDictionary *)nav forVC:(JasonViewController *)v
}
completed:^(UIImage * image, NSError * error, SDImageCacheType cacheType, BOOL finished, NSURL * imageURL) {
if (image) {
- dispatch_async (dispatch_get_main_queue (), ^{
- [self setMenuButtonImage:image
- forButton:btn
- withMenu:right_menu];
- });
+ dispatch_async (dispatch_get_main_queue (), ^{
+ [self setMenuButtonImage:image
+ forButton:btn
+ withMenu:right_menu];
+ });
}
}];
}
@@ -3199,11 +3327,11 @@ - (void)setupHeader:(NSDictionary *)nav forVC:(JasonViewController *)v
}
completed:^(UIImage * image, NSError * error, SDImageCacheType cacheType, BOOL finished, NSURL * imageURL) {
if (image) {
- dispatch_async (dispatch_get_main_queue (), ^{
- [self setLogoImage:image
- withStyle:style
- forVC:v];
- });
+ dispatch_async (dispatch_get_main_queue (), ^{
+ [self setLogoImage:image
+ withStyle:style
+ forVC:v];
+ });
}
}];
}
@@ -3337,6 +3465,7 @@ - (void)setLogoImage:(UIImage *)image withStyle:(NSDictionary *)style forVC:(Jas
UIView * logoView = [[UIView alloc] initWithFrame:frame];
UIImageView * logoImageView = [[UIImageView alloc] initWithImage:image];
+
logoImageView.frame = frame;
[logoView addSubview:logoImageView];
@@ -3386,7 +3515,7 @@ - (void)setupMenuBadge:(BBBadgeBarButtonItem *)barButton forData:(NSDictionary *
}
}
- if (badge[@"text"]) {
+ if (badge[@"text"] && ![[badge[@"text"] description] isEqualToString:@""]) {
barButton.badgeValue = [badge[@"text"] description];
} else {
barButton.badgeValue = @" ";
@@ -3579,14 +3708,14 @@ - (void)setupTabBar:(NSDictionary *)t forVC:(JasonViewController *)v {
if (firstTime) {
// First time loading
if (i == indexOfTab) {
- // for the current tab, simply add the navigationcontrolle to the tabs array
- // no need to create a new VC, etc. because it's already been instantiated
+ // for the current tab, simply add the navigationcontrolle to the tabs array
+ // no need to create a new VC, etc. because it's already been instantiated
tabFound = YES;
- // if the tab URL is same as the currently visible VC's url
+ // if the tab URL is same as the currently visible VC's url
self->VC.tabNeedsRefresh = YES;
[tabs_array addObject:self->navigationController];
} else {
- // for all other tabs, create a new VC and instantiate them, and add them to the tabs array
+ // for all other tabs, create a new VC and instantiate them, and add them to the tabs array
JasonViewController * vc = [[JasonViewController alloc] init];
vc.url = url;
@@ -3604,7 +3733,7 @@ - (void)setupTabBar:(NSDictionary *)t forVC:(JasonViewController *)v {
// If it's not the first time (the tab bars are already visible)
// check the URLs and update if changed.
if ([v.url isEqualToString:url]) {
- // Do nothing
+ // Do nothing
v.tabNeedsRefresh = YES;
tabFound = YES;
} else {
@@ -3690,7 +3819,11 @@ - (BOOL) tabBarController:(UITabBarController *)theTabBarController
if (self->VC.tabNeedsRefresh) {
DTLogDebug (@"Tab %ld Needs Refresh", indexOfTab);
- [[Jason client] call:@{ @"type": @"$reload" }];
+ [((UINavigationController *)viewController) popToRootViewControllerAnimated:NO];
+ self->VC = ((UINavigationController *)viewController).viewControllers.lastObject;
+ self->VC.url = href;
+ [self->VC reload:nil final:NO];
+ [[Jason client] call:@{ @"type": @"$render" }];
return YES;
/* This code contains the logic to refresh.
* the problem is that refreshing more than one time
@@ -3779,9 +3912,9 @@ - (void)setTabBarItem:(UITabBarItem *)item withTab:(NSDictionary *)tab {
}
completed:^(UIImage * i, NSError * error, SDImageCacheType cacheType, BOOL finished, NSURL * imageURL) {
if (i) {
- [self setTabImage:i
- withTab:tab
- andItem:item];
+ [self setTabImage:i
+ withTab:tab
+ andItem:item];
}
}];
}
@@ -3789,7 +3922,7 @@ - (void)setTabBarItem:(UITabBarItem *)item withTab:(NSDictionary *)tab {
[item setTitlePositionAdjustment:UIOffsetMake (0.0, -18.0)];
}
- if (tab[@"badge"]) {
+ if (tab[@"badge"] && ![[tab[@"badge"] description] isEqualToString:@""]) {
[item setBadgeValue:[tab[@"badge"] description]];
}
}
@@ -3816,6 +3949,7 @@ - (void)setTabImage:(UIImage *)image withTab:(NSDictionary *)tab andItem:(UITabB
}
UIImage * newImage = [JasonHelper scaleImage:image ToSize:CGSizeMake (width, height)];
+
dispatch_async (dispatch_get_main_queue (), ^{
[item setImage:newImage];
});
@@ -3947,14 +4081,14 @@ - (void)onOrientationChange:(NSNotification *)notification {
CGRect frame = [UIScreen mainScreen].bounds;
NSDictionary * params = @{
- @"$jason": @{
- @"id": @(device.orientation),
- @"portrait": @(UIDeviceOrientationIsPortrait (device.orientation)),
- @"size": @{
- @"width": @(frame.size.width),
- @"height": @(frame.size.height)
- }
+ @"$jason": @{
+ @"id": @(device.orientation),
+ @"portrait": @(UIDeviceOrientationIsPortrait (device.orientation)),
+ @"size": @{
+ @"width": @(frame.size.width),
+ @"height": @(frame.size.height)
}
+ }
};
DTLogInfo (@"Calling $orientation.changed event %@", params);
@@ -3966,6 +4100,14 @@ - (void)onOrientationChange:(NSNotification *)notification {
# pragma mark - View Linking
- (void)go:(NSDictionary *)href
{
+ DTLogDebug (@"Go to href %@", href);
+
+ // Href should be a dictionary
+ if (![href respondsToSelector:@selector(objectForKey:)]) {
+ DTLogDebug (@"href is not a dictionary %@", href);
+ return;
+ }
+
/*******************************
*
* Linking View to another View
@@ -4080,7 +4222,11 @@ - (void)go:(NSDictionary *)href
DTLogDebug (@"openURL: %@", encodedUrl);
- [[UIApplication sharedApplication] openURL:destination];
+ [[UIApplication sharedApplication] openURL:destination
+ options:@{}
+ completionHandler:^(BOOL success) {
+ DTLogDebug (@"Openned %@", encodedUrl);
+ }];
} else {
DTLogWarning (@"Invalid Url");
}
@@ -4095,11 +4241,11 @@ - (void)go:(NSDictionary *)href
if ([transition isEqualToString:@"replace"]) {
DTLogDebug (@"Replacing the current view");
- /****************************************************************************
- *
- * Replace the current view
- *
- ****************************************************************************/
+ /****************************************************************************
+ *
+ * Replace the current view
+ *
+ ****************************************************************************/
[self unlock];
if (href) {
@@ -4440,11 +4586,11 @@ - (void)exec
memory.executing = NO;
if (memory._stack && memory._stack.count > 0) {
- /****************************************************
- * First, handle conditional cases
- * If the stack contains an array, it must be a conditional. (#if)
- # So run it through 'options' method to generate an actual stack
- ****************************************************/
+ /****************************************************
+ * First, handle conditional cases
+ * If the stack contains an array, it must be a conditional. (#if)
+ # So run it through 'options' method to generate an actual stack
+ ****************************************************/
if ([memory._stack isKindOfClass:[NSArray class]]) {
memory._stack = [self filloutTemplate:memory._stack withData:memory._register];
}
@@ -4538,14 +4684,14 @@ - (void)exec
DTLogDebug (@"Action Call by Type");
NSArray * tokens = [type componentsSeparatedByString:@"."];
- // Jason Core actions: "METHOD" format => Within Jason.m
+ // Jason Core actions: "METHOD" format => Within Jason.m
if (tokens.count == 1) {
if (type.length > 1 && [type hasPrefix:@"$"]) {
NSString * actionName = [type substringFromIndex:1];
SEL method = NSSelectorFromString (actionName);
self.options = [self options];
- // Set 'executing' to YES to prevent other actions from being accidentally executed concurrently
+ // Set 'executing' to YES to prevent other actions from being accidentally executed concurrently
memory.executing = YES;
if ([self respondsToSelector:method]) {
@@ -4632,7 +4778,7 @@ - (void)exec
} }];
}
} else {
- // ignore error: "@ModuleName" -> missing action name
+ // ignore error: "@ModuleName" -> missing action name
DTLogDebug (@"Missing Action Name");
}
}
@@ -4641,7 +4787,7 @@ - (void)exec
DTLogDebug (@"$class.method format");
NSString * className = tokens[0];
- // first take a look at the json file to resolve classname
+ // first take a look at the json file to resolve classname
NSString * resourcePath = [[NSBundle mainBundle] resourcePath];
NSString * jrjson_filename = [NSString stringWithFormat:@"%@/%@.json", resourcePath, className];
NSFileManager * fileManager = [NSFileManager defaultManager];
@@ -4724,7 +4870,7 @@ - (void)exec
// If the stack doesn't include any action to take after success, just finish
if (!memory._stack[@"success"] && !memory._stack[@"error"]) {
- // VC.contentLoaded is NO if the action is $reload (until it returns)
+ // VC.contentLoaded is NO if the action is $reload (until it returns)
if (VC.contentLoaded) {
[self finish];
}
@@ -4783,7 +4929,11 @@ - (void)unlock
[JasonMemory client].locked = NO;
[JasonMemory client].executing = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:@"finishRefreshing" object:nil];
- VC.view.userInteractionEnabled = YES;
+
+ dispatch_async (dispatch_get_main_queue (), ^{
+ self->VC.view.userInteractionEnabled = YES;
+ });
+
[[NSNotificationCenter defaultCenter] postNotificationName:@"unlock" object:nil];
// In case oauth was in process, set it back to No
@@ -4807,7 +4957,7 @@ - (void)cache_view
if (self->VC.original && self->VC.rendered && self->VC.original[@"$jason"][@"head"][@"offline"]) {
DTLogInfo (@"Offline Mode Activated");
- if (![[VC.rendered description] containsString:@"{{"] && ![[self.options description] containsString:@"}}"]) {
+ if (![[self->VC.rendered description] containsString:@"{{"] && ![[self.options description] containsString:@"}}"]) {
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSString * normalized_url = [JasonHelper normalized_url:self->VC.url forOptions:self->VC.options];
normalized_url = [normalized_url stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
@@ -4830,6 +4980,7 @@ - (void)cache_view
// if not offline, delete the file associated with the url
NSString * normalized_url = [JasonHelper normalized_url:self->VC.url forOptions:self->VC.options];
+
normalized_url = [normalized_url stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
diff --git a/xcode/Jasonette/Core/Logger/JasonLogger.m b/xcode/Jasonette/Core/Logger/JasonLogger.m
index 7eca0401..e19fb19b 100644
--- a/xcode/Jasonette/Core/Logger/JasonLogger.m
+++ b/xcode/Jasonette/Core/Logger/JasonLogger.m
@@ -21,14 +21,14 @@ + (void)setupWithLogLevel:(DTLogLevel)level {
[JasonLogger setLogLevel:level];
kLevelNames = @{
- @(DTLogLevelDebug): @"DEBUG",
- @(DTLogLevelInfo): @"INFO",
- @(DTLogLevelAlert): @"ALERT",
- @(DTLogLevelNotice): @"NOTICE",
- @(DTLogLevelError): @"ERROR",
- @(DTLogLevelWarning): @"WARNING",
- @(DTLogLevelCritical): @"CRITICAL",
- @(DTLogLevelEmergency): @"EMERGENCY"
+ @(DTLogLevelDebug): @"DEBUG",
+ @(DTLogLevelInfo): @"INFO",
+ @(DTLogLevelAlert): @"ALERT",
+ @(DTLogLevelNotice): @"NOTICE",
+ @(DTLogLevelError): @"ERROR",
+ @(DTLogLevelWarning): @"WARNING",
+ @(DTLogLevelCritical): @"CRITICAL",
+ @(DTLogLevelEmergency): @"EMERGENCY"
};
}
diff --git a/xcode/Jasonette/Core/Networking/Serializer/JASONResponseSerializer.m b/xcode/Jasonette/Core/Networking/Serializer/JASONResponseSerializer.m
index 9b97fcfa..575191d3 100644
--- a/xcode/Jasonette/Core/Networking/Serializer/JASONResponseSerializer.m
+++ b/xcode/Jasonette/Core/Networking/Serializer/JASONResponseSerializer.m
@@ -13,7 +13,13 @@ - (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)errorPointer
{
- id responseObject = [super responseObjectForResponse:response data:data error:errorPointer];
+ /*
+ * Using NSJSONReadingAllowFragments enables parsing JSON files that are just
+ * a number or string. Accepted JSON can now start with {}, [], "", 0-9.
+ */
+ AFJSONResponseSerializer * serializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingFragmentsAllowed];
+
+ id responseObject = [serializer responseObjectForResponse:response data:data error:errorPointer];
if (*errorPointer) {
NSError * error = *errorPointer;
diff --git a/xcode/Jasonette/Core/Parser/JasonParser.m b/xcode/Jasonette/Core/Parser/JasonParser.m
index e25a50c4..b8bae89b 100644
--- a/xcode/Jasonette/Core/Parser/JasonParser.m
+++ b/xcode/Jasonette/Core/Parser/JasonParser.m
@@ -74,7 +74,7 @@ + (id)parse:(id)data
JSContext * context = [[JSContext alloc] init];
[context setExceptionHandler:^(JSContext * context, JSValue * value) {
DTLogWarning (@"%@", value);
- }];
+ }];
[context evaluateScript:js];
diff --git a/xcode/Jasonette/Core/Views/Badge/UIBarButtonItem+Badge.m b/xcode/Jasonette/Core/Views/Badge/UIBarButtonItem+Badge.m
index 8bb19949..1e980df1 100644
--- a/xcode/Jasonette/Core/Views/Badge/UIBarButtonItem+Badge.m
+++ b/xcode/Jasonette/Core/Views/Badge/UIBarButtonItem+Badge.m
@@ -85,6 +85,7 @@ - (CGSize)badgeExpectedSize
[frameLabel sizeToFit];
CGSize expectedLabelSize = frameLabel.frame.size;
+
return expectedLabelSize;
}
@@ -125,6 +126,7 @@ - (void)updateBadgeValueAnimated:(BOOL)animated
// Animate the size modification if needed
NSTimeInterval duration = animated ? 0.2 : 0;
+
[UIView animateWithDuration:duration
animations:^{
[self updateBadgeFrame];
diff --git a/xcode/Jasonette/Core/Views/JasonViewController.m b/xcode/Jasonette/Core/Views/JasonViewController.m
index 985a95e0..cc4a515c 100644
--- a/xcode/Jasonette/Core/Views/JasonViewController.m
+++ b/xcode/Jasonette/Core/Views/JasonViewController.m
@@ -46,11 +46,11 @@ @interface JasonViewController () {
@implementation JasonViewController
-- (NSDictionary *) style {
- if(!_style) {
+- (NSDictionary *)style {
+ if (!_style) {
_style = @{};
}
-
+
return _style;
}
@@ -195,7 +195,9 @@ - (void)setupIndexPathsForImage:(NSNotification *)notification {
}
- (void)unlock {
- [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
+ dispatch_async (dispatch_get_main_queue (), ^{
+ [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
+ });
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
@@ -627,21 +629,21 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
}
} @catch (NSException * e) {
NSDictionary * item = @{
- @"type": @"vertical",
- @"style": @{
- @"spacing": @"5"
- },
- @"components": @[
- @{
- @"type": @"label",
- @"text": @"Error",
- @"style": @{ @"size": @"30", @"align": @"center", @"padding": @"10" }
- }, @{
- @"type": @"label",
- @"text": @"Something went wrong.",
- @"style": @{ @"size": @"12", @"align": @"center", @"padding": @"10" }
- }
- ]
+ @"type": @"vertical",
+ @"style": @{
+ @"spacing": @"5"
+ },
+ @"components": @[
+ @{
+ @"type": @"label",
+ @"text": @"Error",
+ @"style": @{ @"size": @"30", @"align": @"center", @"padding": @"10" }
+ }, @{
+ @"type": @"label",
+ @"text": @"Something went wrong.",
+ @"style": @{ @"size": @"12", @"align": @"center", @"padding": @"10" }
+ }
+ ]
};
hasError = YES;
return [self getVerticalSectionItem:item forTableView:tableView atIndexPath:indexPath];
@@ -1044,8 +1046,14 @@ - (NSArray *)cleanArray:(NSArray *)arr {
- (void)loadAssets:(NSDictionary *)body {
JasonViewController * weakSelf = self;
+ DTLogDebug (@"Load Assets");
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
- NSArray * keys = [body allKeys];
+ DTLogDebug (@"Body %@", body);
+ NSArray * keys = @[];
+
+ if ([body respondsToSelector:@selector(allKeys)]) {
+ keys = [body allKeys];
+ }
for (NSString * key in keys) {
if ([body[key] isKindOfClass:[NSArray class]]) {
@@ -1064,16 +1072,16 @@ - (void)loadAssets:(NSDictionary *)body {
[self loadAssets:body[key]];
} else {
if ([body[key] isKindOfClass:[NSString class]]) {
- // String => Terminal Node
+ // String => Terminal Node
if ([key isEqualToString:@"url"]) {
- // it's a url!
- // see if it's an image type
+ // it's a url!
+ // see if it's an image type
if (body[@"type"]) {
if ([body[@"type"] isEqualToString:@"image"] || [body[@"type"] isEqualToString:@"button"]) {
if (body[@"style"]) {
- // [Image load optimization] Don't load assets if
- // 1. height exists or
- // 2. width + ratio exist
+ // [Image load optimization] Don't load assets if
+ // 1. height exists or
+ // 2. width + ratio exist
if (body[@"style"][@"height"]) {
return;
} else if (body[@"style"][@"width"] && body[@"style"][@"ratio"]) {
@@ -1108,25 +1116,25 @@ - (void)loadAssets:(NSDictionary *)body {
self->download_image_counter--;
if (!error) {
- JasonComponentFactory.imageLoaded[url] = [NSValue valueWithCGSize:i.size];
+ JasonComponentFactory.imageLoaded[url] = [NSValue valueWithCGSize:i.size];
}
// [self.tableView visibleCells];
dispatch_async (dispatch_get_main_queue (), ^{
- NSArray * indexPathArray = weakSelf.tableView.indexPathsForVisibleRows;
- NSMutableSet * visibleIndexPaths = [[NSMutableSet alloc] initWithArray:indexPathArray];
- [visibleIndexPaths intersectSet:(NSSet *)self->indexPathsForImage[url]];
-
- if (visibleIndexPaths.count > 0) {
- [weakSelf.tableView reloadData];
- }
-
- if (!self->top_aligned) {
- if (self->download_image_counter == 0) {
- [weakSelf scrollToBottom];
- }
- }
- });
+ NSArray * indexPathArray = weakSelf.tableView.indexPathsForVisibleRows;
+ NSMutableSet * visibleIndexPaths = [[NSMutableSet alloc] initWithArray:indexPathArray];
+ [visibleIndexPaths intersectSet:(NSSet *)self->indexPathsForImage[url]];
+
+ if (visibleIndexPaths.count > 0) {
+ [weakSelf.tableView reloadData];
+ }
+
+ if (!self->top_aligned) {
+ if (self->download_image_counter == 0) {
+ [weakSelf scrollToBottom];
+ }
+ }
+ });
}];
}
}
@@ -1385,8 +1393,8 @@ - (void)setupHeader:(NSDictionary *)body
NSDictionary * header = body[@"header"];
if (header) {
- // only handles components specific to TableView (search/tabs).
- // common component (menu) is handled in Jason.m
+ // only handles components specific to TableView (search/tabs).
+ // common component (menu) is handled in Jason.m
tabs = nil;
for (NSString * type in [header allKeys]) {
@@ -1549,6 +1557,7 @@ - (void)setupSections:(NSDictionary *)body
id weakSelf = self;
+
[weakSelf loadAssets:body];
if (self.events[@"$pull"]) {
@@ -1591,8 +1600,20 @@ - (void)reloadSections:(NSArray *)sections
}
for (NSDictionary * section in self.sections) {
- NSMutableDictionary * header = section[@"header"];
- NSNumber * rowcount_for_section = [NSNumber numberWithLong:[section[@"items"] count]];
+ NSMutableDictionary * header = [@{} mutableCopy];
+ NSArray * items = @[];
+
+ if ([section respondsToSelector:@selector(objectForKey:)]) {
+ if (section[@"header"]) {
+ header = section[@"header"];
+ }
+
+ if (section[@"items"] && [section[@"items"] respondsToSelector:@selector(count)]) {
+ items = section[@"items"];
+ }
+ }
+
+ NSNumber * rowcount_for_section = [NSNumber numberWithLong:[items count]];
[rowcount addObject:rowcount_for_section];
total_rowcount = total_rowcount + [rowcount_for_section longValue];
@@ -1600,7 +1621,7 @@ - (void)reloadSections:(NSArray *)sections
// No header
[headers addObject:@{}];
} else {
- [headers addObject:section[@"header"]];
+ [headers addObject:header];
}
}
@@ -1624,16 +1645,16 @@ - (void)setupFooter:(NSDictionary *)body
CGFloat originalHeight = original_height;
[self.view addKeyboardPanningWithActionHandler:^(CGRect keyboardFrameInView, BOOL opening, BOOL closing)
- {
- CGFloat m = MIN (originalHeight, keyboardFrameInView.origin.y);
-
- if (opening || (closing && m >= weakSelf.view.frame.size.height)) {
- CGRect newViewFrame = CGRectMake (weakSelf.view.frame.origin.x,
- weakSelf.view.frame.origin.y,
- weakSelf.view.frame.size.width, m);
- weakSelf.view.frame = newViewFrame;
- }
- }];
+ {
+ CGFloat m = MIN (originalHeight, keyboardFrameInView.origin.y);
+
+ if (opening || (closing && m >= weakSelf.view.frame.size.height)) {
+ CGRect newViewFrame = CGRectMake (weakSelf.view.frame.origin.x,
+ weakSelf.view.frame.origin.y,
+ weakSelf.view.frame.size.width, m);
+ weakSelf.view.frame = newViewFrame;
+ }
+ }];
// textfield logic
@@ -1673,22 +1694,22 @@ - (void)setupFooter:(NSDictionary *)body
// input field styling
if (field[@"style"]) {
- // PHFComposeBarView hack to find relevant views and apply style
- // [JasonHelper force_background:@"#000000" intoView:composeBarView];
+ // PHFComposeBarView hack to find relevant views and apply style
+ // [JasonHelper force_background:@"#000000" intoView:composeBarView];
for (UIView * v in self.composeBarView.subviews) {
for (UIView * vv in v.subviews) {
if ([vv isKindOfClass:[UITextView class]]) {
vv.superview.layer.borderWidth = 0;
for (UIView * vvv in vv.superview.subviews) {
- // textfield background
+ // textfield background
if (field[@"style"][@"background"]) {
vvv.backgroundColor = [JasonHelper colorwithHexString:field[@"style"][@"background"] alpha:1.0];
}
- // placeholder color
+ // placeholder color
if ([vvv isKindOfClass:[UILabel class]]) {
- // placeholder label
+ // placeholder label
((UILabel *)vvv).textColor = [JasonHelper colorwithHexString:field[@"style"][@"color:placeholder"] alpha:1.0];
}
}
@@ -1733,17 +1754,17 @@ - (void)setupFooter:(NSDictionary *)body
completed:^(UIImage * image, NSError * error, SDImageCacheType cacheType, BOOL finished, NSURL * imageURL) {
// colorize
if (self->chat_input[@"left"][@"style"] && self->chat_input[@"left"][@"style"][@"color"]) {
- UIColor * newColor = [JasonHelper colorwithHexString:self->chat_input[@"left"][@"style"][@"color"]
- alpha:1.0];
- image = [JasonHelper colorize:image
- into:newColor];
+ UIColor * newColor = [JasonHelper colorwithHexString:self->chat_input[@"left"][@"style"][@"color"]
+ alpha:1.0];
+ image = [JasonHelper colorize:image
+ into:newColor];
}
- UIImage * resizedImage = [JasonHelper scaleImage:image
- ToSize:CGSizeMake (30, 30)];
+ UIImage * resizedImage = [JasonHelper scaleImage:image
+ ToSize:CGSizeMake (30, 30)];
dispatch_async (dispatch_get_main_queue (), ^{
- [self.composeBarView setUtilityButtonImage:resizedImage];
- });
+ [self.composeBarView setUtilityButtonImage:resizedImage];
+ });
}
];
});
@@ -1764,16 +1785,16 @@ - (void)setupFooter:(NSDictionary *)body
for (UIButton * button in buttons) {
if ([button.subviews.firstObject isKindOfClass:[UILabel class]]) {
- // set "color"
+ // set "color"
if (chat_input[@"right"][@"style"] && chat_input[@"right"][@"style"][@"color"]) {
[button setTitleColor:[JasonHelper colorwithHexString:chat_input[@"right"][@"style"][@"color"] alpha:1.0] forState:UIControlStateNormal];
}
- // set "color:disabled"
+ // set "color:disabled"
if (chat_input[@"right"][@"style"] && chat_input[@"right"][@"style"][@"color:disabled"]) {
[button setTitleColor:[JasonHelper colorwithHexString:chat_input[@"right"][@"style"][@"color:disabled"] alpha:1.0] forState:UIControlStateDisabled];
} else {
- // default
+ // default
[button setTitleColor:[JasonHelper colorwithHexString:chat_input[@"right"][@"style"][@"color"] alpha:1.0] forState:UIControlStateDisabled];
}
}
diff --git a/xcode/Jasonette/Info.plist b/xcode/Jasonette/Info.plist
index 90a61e59..ffc0d5ef 100644
--- a/xcode/Jasonette/Info.plist
+++ b/xcode/Jasonette/Info.plist
@@ -43,6 +43,8 @@
used to access photo library
NSLocationAlwaysUsageDescription
used to access location api
+ NSUserTrackingUsageDescription
+ Your reason, why you want to track the user
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
diff --git a/xcode/Jasonette/Services/$agent/JasonAgentService.m b/xcode/Jasonette/Services/$agent/JasonAgentService.m
index 4ae4fca0..0ed292dc 100644
--- a/xcode/Jasonette/Services/$agent/JasonAgentService.m
+++ b/xcode/Jasonette/Services/$agent/JasonAgentService.m
@@ -73,8 +73,8 @@ - (void)userContentController:(WKUserContentController *)userContentController d
// to keep track of the source agent so that a response
// can be sent back to the $source later.
event[@"$source"] = @{
- @"id": identifier,
- @"nonce": m[@"nonce"]
+ @"id": identifier,
+ @"nonce": m[@"nonce"]
};
DTLogDebug (@"Requesting %@", event);
@@ -140,6 +140,7 @@ - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigat
// Inject agent.js into agent context
NSString * identifier = webView.payload[@"identifier"];
+
DTLogDebug (@"Injecting agent.js into context %@", identifier);
NSString * raw = [JasonHelper read_local_file:@"file://agent.js"];
@@ -147,7 +148,11 @@ - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigat
stringWithFormat:@"$agent.interface = window.webkit.messageHandlers[\"%@\"];\n",
identifier];
- NSString * summon = [raw stringByAppendingString:interface];
+ DTLogDebug (@"Injecting custom.js into context %@", identifier);
+ NSString * custom = [JasonHelper read_local_file:@"file://custom.js"];
+ NSString * summon = [[raw
+ stringByAppendingString:interface]
+ stringByAppendingString:custom];
webView.payload[@"state"] = @"rendered";
@@ -156,7 +161,7 @@ - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigat
DTLogInfo (@"Injected $agent into context");
if (error) {
- DTLogWarning (@"%@", error);
+ DTLogWarning (@"%@", error);
}
}];
@@ -188,20 +193,20 @@ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigati
NSDictionary * resolved;
NSMutableDictionary * data_stub = [[[Jason client] variables] mutableCopy];
- // Prepare the url to return
+ // Prepare the url to return
NSString * url;
if ([navigationAction.request.URL.absoluteString hasPrefix:@"file://"]) {
NSString * resourcePath = [[NSBundle mainBundle] resourcePath];
if ([navigationAction.request.URL.absoluteString containsString:resourcePath]) {
- // it's an internal path. Convert it to regular file format
+ // it's an internal path. Convert it to regular file format
url = [navigationAction.request.URL.absoluteString stringByReplacingOccurrencesOfString:resourcePath withString:@""];
- // Turn 'file:///' into 'file://'
+ // Turn 'file:///' into 'file://'
url = [url stringByReplacingOccurrencesOfString:@"file:///" withString:@"file://"];
} else {
- // it's a regular file url, like: file://local.json
+ // it's a regular file url, like: file://local.json
url = navigationAction.request.URL.absoluteString;
}
} else {
@@ -229,8 +234,8 @@ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigati
decisionHandler (WKNavigationActionPolicyAllow);
} else {
if ([navigationAction.sourceFrame isEqual:navigationAction.targetFrame]) {
- // normal navigation
- // Need to handle JASON action
+ // normal navigation
+ // Need to handle JASON action
if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
decisionHandler (WKNavigationActionPolicyCancel);
@@ -297,7 +302,7 @@ - (void)refresh:(WKWebView *)agent withOptions:(NSDictionary *)options {
if (url) {
// contains "url" attribute
if ([url containsString:@"file://"]) {
- // File URL
+ // File URL
DTLogDebug (@"Loading with Local File %@", url);
NSString * path = [JasonHelper get_local_path:url];
NSURL * u = [NSURL fileURLWithPath:path isDirectory:NO];
@@ -307,9 +312,24 @@ - (void)refresh:(WKWebView *)agent withOptions:(NSDictionary *)options {
}
} else {
// Remote URL
- DTLogDebug (@"Loading Remote URL %@", url);
+ DTLogDebug (@"Loading Remote URL %@ With Options %@", url, options);
NSURL * nsurl = [NSURL URLWithString:url];
- NSURLRequest * nsrequest = [NSURLRequest requestWithURL:nsurl];
+ NSMutableURLRequest * nsrequest = [[NSURLRequest requestWithURL:nsurl] mutableCopy];
+
+ NSDictionary * innerOptions = options[@"options"];
+
+ if (innerOptions) {
+ NSDictionary * headers = innerOptions[@"header"];
+
+ if (!headers) {
+ headers = innerOptions[@"headers"];
+ }
+
+ for (NSString * key in headers) {
+ DTLogDebug (@"Using Header %@ : %@", key, headers[key], nsrequest.URL);
+ [nsrequest addValue:headers[key] forHTTPHeaderField:key];
+ }
+ }
if (shouldReload) {
[agent loadRequest:nsrequest];
@@ -342,6 +362,7 @@ - (void)refresh:(WKWebView *)agent withOptions:(NSDictionary *)options {
}
JasonViewController * vc = (JasonViewController *)[[Jason client] getVC];
+
[vc.view setNeedsDisplay];
}
@@ -354,13 +375,14 @@ - (void)refresh:(NSDictionary *)options {
// 1. Initialize
if (vc.agents && vc.agents[identifier]) {
- // Already existing agent, juse reuse the old one
+ // Already existing agent, juse reuse the old one
WKWebView * agent = vc.agents[identifier];
agent.payload[@"state"] = @"empty";
NSMutableDictionary * new_options = [options mutableCopy];
new_options[@"text"] = agent.payload[@"text"];
new_options[@"url"] = agent.payload[@"url"];
new_options[@"action"] = agent.payload[@"action"];
+ new_options[@"options"] = agent.payload[@"options"];
[self refresh:agent withOptions:new_options];
[[Jason client] success];
} else {
@@ -456,16 +478,14 @@ - (WKWebView *)setup:(NSDictionary *)options withId:(NSString *)identifier {
forKeyPath:NSStringFromSelector (@selector(estimatedProgress))
options:NSKeyValueObservingOptionNew
context:NULL];
-
} else {
// This helper will allow to continue execution normally.
- DTLogDebug(@"iOS <= 10 detected. Using AutoRemoveObserver for agent %@", identifier);
+ DTLogDebug (@"iOS <= 10 detected. Using AutoRemoveObserver for agent %@", identifier);
[INTUAutoRemoveObserver addObserver:self
- forKeyPath:NSStringFromSelector (@selector(estimatedProgress))
- options:NSKeyValueObservingOptionNew
+ forKeyPath:NSStringFromSelector (@selector(estimatedProgress))
+ options:NSKeyValueObservingOptionNew
context:(__bridge void * _Nullable)(identifier)];
}
-
agent.hidden = YES;
@@ -587,12 +607,14 @@ - (void)inject:(NSDictionary *)options {
}
- (void)inject:(NSString *)code into:(WKWebView *)agent {
- [agent evaluateJavaScript:code
- completionHandler:^(id _Nullable res, NSError * _Nullable error) {
- // Step 2. Execute the method with params
- DTLogDebug (@"Injected code into agent");
- [[Jason client] success];
- }];
+ dispatch_async (dispatch_get_main_queue (), ^{
+ [agent evaluateJavaScript:code
+ completionHandler:^(id _Nullable res, NSError * _Nullable error) {
+ // Step 2. Execute the method with params
+ DTLogDebug (@"Injected code into agent");
+ [[Jason client] success];
+ }];
+ });
}
- (void)request:(NSDictionary *)options {
@@ -623,17 +645,19 @@ - (void)request:(NSDictionary *)options {
agent.payload[@"$source"] = options[@"$source"];
// Evaluate JavaScript on the agent
- [agent evaluateJavaScript:callstring
- completionHandler:^(id _Nullable res, NSError * _Nullable error) {
- // Don't process return value.
- // Instead all communication back to Jasonette is taken care of by an explicit $agent.response() call
- if (error) {
- DTLogWarning (@"%@", error);
- agent.payload[@"pending"] = options;
- // The agent might not be ready. Put it in a queue.
- }
- }];
- // Agent doesn't exist, return with the error callback
+ dispatch_async (dispatch_get_main_queue (), ^{
+ [agent evaluateJavaScript:callstring
+ completionHandler:^(id _Nullable res, NSError * _Nullable error) {
+ // Don't process return value.
+ // Instead all communication back to Jasonette is taken care of by an explicit $agent.response() call
+ if (error) {
+ DTLogWarning (@"%@", error);
+ agent.payload[@"pending"] = options;
+ // The agent might not be ready. Put it in a queue.
+ }
+ }];
+ // Agent doesn't exist, return with the error callback
+ });
} else {
[[Jason client] error];
}
@@ -646,23 +670,23 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
[progressView setAlpha:1.0f];
[progressView setProgress:((WKWebView *)object).estimatedProgress animated:YES];
DTLogDebug (@"%f", progressView.progress);
-
- WKWebView * webview = (WKWebView *) object;
-
+
+ WKWebView * webview = (WKWebView *)object;
+
BOOL iOS11 = NO;
-
- if(@available(iOS 11, *)){
+
+ if (@available(iOS 11, *)) {
iOS11 = YES;
}
-
+
if (!iOS11 && context != NULL) {
NSString * identifier = (__bridge NSString *)(context);
- DTLogDebug(@"iOS <= 10 %@", identifier);
+ DTLogDebug (@"iOS <= 10 %@", identifier);
JasonViewController * vc = (JasonViewController *)[[Jason client] getVC];
webview = vc.agents[identifier];
}
- if(webview && [webview respondsToSelector:@selector(estimatedProgress)]){
+ if (webview && [webview respondsToSelector:@selector(estimatedProgress)]) {
if (webview.estimatedProgress >= 1.0f) {
[UIView animateWithDuration:0.3
delay:0.3
@@ -671,8 +695,8 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
[progressView setAlpha:0.0f];
}
completion:^(BOOL finished) {
- [progressView setProgress:0.0f
- animated:NO];
+ [progressView setProgress:0.0f
+ animated:NO];
}];
}
}
diff --git a/xcode/Jasonette/Services/$push/JasonPushService.m b/xcode/Jasonette/Services/$push/JasonPushService.m
index 89f0aba3..6ef8f293 100644
--- a/xcode/Jasonette/Services/$push/JasonPushService.m
+++ b/xcode/Jasonette/Services/$push/JasonPushService.m
@@ -13,23 +13,67 @@
@implementation JasonPushService
+
+- (nonnull NSDictionary *)normalize:(nullable NSDictionary *)userInfo {
+ if (!userInfo) {
+ return @{};
+ }
+
+ // Check if the userInfo is a string
+ NSDictionary * info = userInfo[@"href"];
+
+ if (!info) {
+ info = userInfo[@"action"];
+ }
+
+ if ([info respondsToSelector:@selector(containsString:)]) {
+ // Maybe the userInfo is a json object in a string
+ // This can be a case when a notification payload is sent via Firebase or similar
+ // That only strings are allowed in the data attribute of the notification.
+ NSString * json = (NSString *)info;
+ NSError * error = nil;
+ id jsonObject = [NSJSONSerialization
+ JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding]
+ options:kNilOptions
+ error:&error];
+
+ DTLogDebug (@"Detected and parsed json string in userInfo %@", jsonObject);
+ info = jsonObject;
+
+ if (error) {
+ DTLogDebug (@"%@", error);
+ info = nil;
+ }
+ }
+
+ NSMutableDictionary * normalized = [userInfo mutableCopy];
+
+ if (info) {
+ if (normalized[@"href"]) {
+ normalized[@"href"] = info;
+ } else if (normalized[@"action"]) {
+ normalized[@"action"] = info;
+ }
+ }
+
+ return [normalized copy];
+}
+
- (void)initialize:(NSDictionary *)launchOptions
{
DTLogDebug (@"initialize");
#ifdef PUSH
- NSDictionary * userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
+ NSDictionary * userInfo = [self normalize:[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]];
- if (userInfo) {
- if (userInfo[@"href"]) {
- [[Jason client] call:@{
- @"type": @"$href",
- @"options": userInfo[@"href"]
- }];
- } else if (userInfo[@"action"]) {
- [[Jason client] call:userInfo[@"action"]];
- }
+ if (userInfo[@"href"]) {
+ [[Jason client] call:@{
+ @"type": @"$href",
+ @"options": userInfo[@"href"]
+ }];
+ } else if (userInfo[@"action"]) {
+ [[Jason client] call:userInfo[@"action"]];
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"onRemoteNotification:" object:nil];
@@ -56,7 +100,7 @@ - (void)process:(NSDictionary *)payload
DTLogDebug (@"Calling $push.onmessage event");
[[Jason client]
call:events[@"$push.onmessage"]
- with:@{ @"$jason": payload }];
+ with:@{ @"$jason": [self normalize:payload] }];
}
}
}
@@ -67,14 +111,16 @@ - (void)onRemoteNotification:(NSNotification *)notification
}
- (void)onRemoteNotificationDeviceRegistered:(NSNotification *)notification {
- NSDictionary * payload = notification.userInfo;
+ NSDictionary * payload = [self normalize:notification.userInfo];
NSDictionary * events = [[[Jason client] getVC] valueForKey:@"events"];
if (events) {
if (events[@"$push.onregister"]) {
- NSDictionary * params = @{ @"$jason":
- @{ @"token":
- payload[@"token"] } };
+ NSDictionary * params = @{ @"$jason": @{ @"token": @"" } };
+
+ if (payload && payload[@"token"]) {
+ params = @{ @"$jason": payload };
+ }
DTLogDebug (@"Calling $push.onregister event with params %@", params);
@@ -94,12 +140,14 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti
if (response.notification.request.content.userInfo) {
DTLogDebug (@"Received Notification Response %@", response.notification.request.content.userInfo);
- if (response.notification.request.content.userInfo[@"href"]) {
+ NSDictionary * userInfo = [self normalize:response.notification.request.content.userInfo];
+
+ if (userInfo[@"href"]) {
DTLogDebug (@"Show href");
- [[Jason client] go:response.notification.request.content.userInfo[@"href"]];
- } else if (response.notification.request.content.userInfo[@"action"]) {
+ [[Jason client] go:userInfo[@"href"]];
+ } else if (userInfo[@"action"]) {
DTLogDebug (@"Executing Action");
- [[Jason client] call:response.notification.request.content.userInfo[@"action"]];
+ [[Jason client] call:userInfo[@"action"]];
}
}
diff --git a/xcode/Jasonette/Views/Horizontal/JasonHorizontalSection.m b/xcode/Jasonette/Views/Horizontal/JasonHorizontalSection.m
index 15c789a1..b2d1aaa2 100644
--- a/xcode/Jasonette/Views/Horizontal/JasonHorizontalSection.m
+++ b/xcode/Jasonette/Views/Horizontal/JasonHorizontalSection.m
@@ -16,6 +16,7 @@ - (void)awakeFromNib {
[super awakeFromNib];
UICollectionViewFlowLayout * flowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
+
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flowLayout.minimumLineSpacing = 0.0;
flowLayout.minimumInteritemSpacing = 0.0;
diff --git a/xcode/Jasonette/Views/Layer/JasonLayer.m b/xcode/Jasonette/Views/Layer/JasonLayer.m
index 0ff059fc..c5b0deb6 100644
--- a/xcode/Jasonette/Views/Layer/JasonLayer.m
+++ b/xcode/Jasonette/Views/Layer/JasonLayer.m
@@ -77,20 +77,20 @@ + (NSArray *)setupLayers:(NSDictionary *)body withView:(UIView *)rootView
CGSize size = image.size;
if (size.width > 0 && size.height > 0) {
- if (layer[@"style"]) {
- [self setStyle:layer[@"style"]
- ForLayerChild:layerChild
- ofSize:[NSValue valueWithCGSize:size]];
-
- if (layer[@"style"][@"color"]) {
- // Setting tint color for an image
- UIColor * newColor = [JasonHelper colorwithHexString:layer[@"style"][@"color"]
- alpha:1.0];
- UIImage * newImage = [JasonHelper colorize:image
- into:newColor];
- layerChild.image = newImage;
- }
- }
+ if (layer[@"style"]) {
+ [self setStyle:layer[@"style"]
+ ForLayerChild:layerChild
+ ofSize:[NSValue valueWithCGSize:size]];
+
+ if (layer[@"style"][@"color"]) {
+ // Setting tint color for an image
+ UIColor * newColor = [JasonHelper colorwithHexString:layer[@"style"][@"color"]
+ alpha:1.0];
+ UIImage * newImage = [JasonHelper colorize:image
+ into:newColor];
+ layerChild.image = newImage;
+ }
+ }
}
}];
}
@@ -174,6 +174,7 @@ + (void)addGestureRecognizersTo:(UIView *)view withStyle:(NSDictionary *)style {
}
UITapGestureRecognizer * singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(layerTap:)];
+
[view addGestureRecognizer:singleFingerTap];
}
@@ -327,6 +328,7 @@ + (NSMutableDictionary *)applyStylesheet:(NSDictionary *)item {
}
NSMutableDictionary * stylized_item = [item mutableCopy];
+
stylized_item[@"style"] = new_style;
return stylized_item;
}
diff --git a/xcode/Jasonette/Views/Layout/JasonLayout.m b/xcode/Jasonette/Views/Layout/JasonLayout.m
index 528e3d1e..c7bf2533 100644
--- a/xcode/Jasonette/Views/Layout/JasonLayout.m
+++ b/xcode/Jasonette/Views/Layout/JasonLayout.m
@@ -72,9 +72,9 @@ + (NSDictionary *)fill:(UIStackView *)layout with:(NSDictionary *)item atIndexPa
// This means it's a single element layout
// And therefore needs to be wrapped inside a simple horizontal layout
NSDictionary * wrappedItem = @{
- @"type": @"vertical",
- @"style": style,
- @"components": @[item]
+ @"type": @"vertical",
+ @"style": style,
+ @"components": @[item]
};
layout = [JasonLayout fillChildLayout:layout with:wrappedItem atIndexPath:indexPath withForm:form];
}
@@ -288,14 +288,14 @@ + (UIStackView *)fillChildLayout:(UIStackView *)layout with:(NSDictionary *)item
}
NSDictionary * alignment_map = @{
- @"fill": @(UIStackViewAlignmentFill),
- @"firstbaseline": @(UIStackViewAlignmentFirstBaseline),
- @"lastbaseline": @(UIStackViewAlignmentLastBaseline),
- @"left": @(UIStackViewAlignmentLeading),
- @"top": @(UIStackViewAlignmentTop),
- @"right": @(UIStackViewAlignmentTrailing),
- @"bottom": @(UIStackViewAlignmentBottom),
- @"center": @(UIStackViewAlignmentCenter)
+ @"fill": @(UIStackViewAlignmentFill),
+ @"firstbaseline": @(UIStackViewAlignmentFirstBaseline),
+ @"lastbaseline": @(UIStackViewAlignmentLastBaseline),
+ @"left": @(UIStackViewAlignmentLeading),
+ @"top": @(UIStackViewAlignmentTop),
+ @"right": @(UIStackViewAlignmentTrailing),
+ @"bottom": @(UIStackViewAlignmentBottom),
+ @"center": @(UIStackViewAlignmentCenter)
};
if (style[@"align"]) {
@@ -304,11 +304,11 @@ + (UIStackView *)fillChildLayout:(UIStackView *)layout with:(NSDictionary *)item
}
NSDictionary * distribution_map = @{
- @"fill": @(UIStackViewDistributionFill),
- @"equalsize": @(UIStackViewDistributionFillEqually),
- @"proportional": @(UIStackViewDistributionFillProportionally),
- @"equalspace": @(UIStackViewDistributionEqualSpacing),
- @"equalcentertocenter": @(UIStackViewDistributionEqualCentering)
+ @"fill": @(UIStackViewDistributionFill),
+ @"equalsize": @(UIStackViewDistributionFillEqually),
+ @"proportional": @(UIStackViewDistributionFillProportionally),
+ @"equalspace": @(UIStackViewDistributionEqualSpacing),
+ @"equalcentertocenter": @(UIStackViewDistributionEqualCentering)
};
if (style[@"distribution"]) {
@@ -403,6 +403,7 @@ + (NSMutableDictionary *)applyStylesheet:(NSDictionary *)item {
}
NSMutableDictionary * stylized_item = [item mutableCopy];
+
stylized_item[@"style"] = new_style;
return stylized_item;
}
diff --git a/xcode/Podfile b/xcode/Podfile
index 9ee3adbe..39250778 100644
--- a/xcode/Podfile
+++ b/xcode/Podfile
@@ -1,11 +1,11 @@
-platform :ios, '9.0'
-#inhibit_all_warnings!
+platform :ios, '12.0'
+inhibit_all_warnings!
use_frameworks!
target 'Jasonette' do
# System
- pod 'UICKeyChainStore', :git => 'https://github.com/jasonelle/ios-pod-uickeychainstore.git', :branch => 'jasonelle'
+ pod 'UICKeyChainStore', :git => 'https://github.com/jasonelle/ios-pod-uickeychainstore.git', :branch => 'jasonelle', :inhibit_warnings => true
pod 'libPhoneNumber-iOS', :git => 'https://github.com/jasonelle/ios-pod-libphonenumber.git', :branch => 'jasonelle'
pod 'DTFoundation', :git => 'https://github.com/jasonelle/ios-pod-dtfoundation.git', :branch => 'jasonelle'
pod 'DTCoreText', :git => 'https://github.com/jasonelle/ios-pod-dtcoretext.git', :branch => 'jasonelle'
@@ -25,14 +25,14 @@ target 'Jasonette' do
pod 'SocketRocket', :git => 'https://github.com/jasonelle/ios-pod-socketrocket.git', :branch => 'jasonelle'
pod 'AFNetworking', :git => 'https://github.com/jasonelle/ios-pod-afnetworking.git', :branch => 'jasonelle'
pod 'TDOAuth'
- pod 'AFOAuth2Manager'
+ pod 'AFOAuth2Manager', :git => 'https://github.com/jasonelle/ios-pod-afoauth2manager.git', :branch => 'jasonelle'
## TODO: Update to latest version
- pod 'SDWebImage', :git => 'https://github.com/jasonelle/ios-pod-sdwebimage.git', :tag => '3.8.1', :inhibit_warnings => true
+ pod 'SDWebImage', :git => 'https://github.com/jasonelle/ios-pod-sdwebimage.git', :tag => '3.8.3', :inhibit_warnings => true
# Audio
pod 'IQAudioRecorderController'
- pod 'FreeStreamer', :git => 'https://github.com/jasonelle/ios-pod-freestreamer.git', :branch => 'jasonelle'
+ pod 'FreeStreamer', :git => 'https://github.com/jasonelle/ios-pod-freestreamer.git', :branch => 'jasonelle', :inhibit_warnings => true
# Views
pod 'MBProgressHUD', '~> 1.0'
@@ -46,7 +46,7 @@ target 'Jasonette' do
pod 'BBBadgeBarButtonItem'
pod 'REMenu', :git => 'https://github.com/jasonelle/ios-pod-remenu.git', :branch => 'jasonelle'
- pod 'JDStatusBarNotification' # $util.toast
+ pod 'JDStatusBarNotification', :git => 'https://github.com/jasonelle/ios-pod-jdstatusbarnotification.git', :inhibit_warnings => true # $util.toast
pod 'HMSegmentedControl', :git => 'https://github.com/jasonelle/ios-pod-hmsegmentedcontrol.git', :branch => 'jasonelle'
pod 'SWFrameButton'
diff --git a/xcode/Podfile.lock b/xcode/Podfile.lock
index a9c769df..ba153f26 100644
--- a/xcode/Podfile.lock
+++ b/xcode/Podfile.lock
@@ -1,21 +1,21 @@
PODS:
- - AFNetworking (3.2.1):
- - AFNetworking/NSURLSession (= 3.2.1)
- - AFNetworking/Reachability (= 3.2.1)
- - AFNetworking/Security (= 3.2.1)
- - AFNetworking/Serialization (= 3.2.1)
- - AFNetworking/UIKit (= 3.2.1)
- - AFNetworking/NSURLSession (3.2.1):
+ - AFNetworking (4.0.1):
+ - AFNetworking/NSURLSession (= 4.0.1)
+ - AFNetworking/Reachability (= 4.0.1)
+ - AFNetworking/Security (= 4.0.1)
+ - AFNetworking/Serialization (= 4.0.1)
+ - AFNetworking/UIKit (= 4.0.1)
+ - AFNetworking/NSURLSession (4.0.1):
- AFNetworking/Reachability
- AFNetworking/Security
- AFNetworking/Serialization
- - AFNetworking/Reachability (3.2.1)
- - AFNetworking/Security (3.2.1)
- - AFNetworking/Serialization (3.2.1)
- - AFNetworking/UIKit (3.2.1):
+ - AFNetworking/Reachability (4.0.1)
+ - AFNetworking/Security (4.0.1)
+ - AFNetworking/Serialization (4.0.1)
+ - AFNetworking/UIKit (4.0.1):
- AFNetworking/NSURLSession
- - AFOAuth2Manager (3.0.0):
- - AFNetworking/NSURLSession (~> 3.0)
+ - AFOAuth2Manager (3.0.1):
+ - AFNetworking/NSURLSession (~> 4.0)
- AHKActionSheet (0.5.4)
- APAddressBook (0.3.2):
- APAddressBook/Core (= 0.3.2)
@@ -87,17 +87,17 @@ PODS:
- DTFoundation/Core
- DTFoundation/UIKit_BlocksAdditions (1.7.14):
- DTFoundation/Core
- - FLEX (3.0.0)
+ - FLEX (4.1.1)
- FreeStreamer (4.0.0):
- Reachability (~> 3.0)
- HMSegmentedControl (1.5.5)
- INTULocationManager (4.3.2)
- IQAudioRecorderController (1.2.3):
- SCSiriWaveformView
- - JDStatusBarNotification (1.6.0)
+ - JDStatusBarNotification (1.6.1)
- JSCoreBom (1.1.2)
- libPhoneNumber-iOS (0.9.15)
- - MBProgressHUD (1.1.0)
+ - MBProgressHUD (1.2.0)
- NSGIF (1.2.4)
- NSHash (1.2.0)
- OMGHTTPURLRQ (3.2.4):
@@ -107,24 +107,24 @@ PODS:
- OMGHTTPURLRQ/FormURLEncode
- OMGHTTPURLRQ/UserAgent
- OMGHTTPURLRQ/UserAgent (3.2.4)
- - PHFComposeBarView (2.0.2):
+ - PHFComposeBarView (2.1.0):
- PHFDelegateChain (~> 1.0)
- PHFDelegateChain (1.0.1)
- Reachability (3.2)
- REMenu (1.10)
- - RMActionController (1.0.5)
- - RMDateSelectionViewController (2.0.3):
- - RMActionController (~> 1.0.5)
+ - RMActionController (1.3.1)
+ - RMDateSelectionViewController (2.3.1):
+ - RMActionController (~> 1.3.1)
- SBJson (5.0.0)
- SCSiriWaveformView (1.1.1)
- - SDWebImage (3.8.1):
- - SDWebImage/Core (= 3.8.1)
- - SDWebImage/Core (3.8.1)
- - SocketRocket (0.5.1)
+ - SDWebImage (3.8.3):
+ - SDWebImage/Core (= 3.8.3)
+ - SDWebImage/Core (3.8.3)
+ - SocketRocket (0.5.2)
- SWFrameButton (1.2.2)
- SWTableViewCell (0.3.7)
- - SZTextView (1.2.2)
- - TDOAuth (1.1.2):
+ - SZTextView (1.3.0)
+ - TDOAuth (1.3.0):
- OMGHTTPURLRQ/UserAgent
- TTTAttributedLabel (2.0.0)
- TWMessageBarManager (1.8.1)
@@ -132,7 +132,7 @@ PODS:
DEPENDENCIES:
- AFNetworking (from `https://github.com/jasonelle/ios-pod-afnetworking.git`, branch `jasonelle`)
- - AFOAuth2Manager
+ - AFOAuth2Manager (from `https://github.com/jasonelle/ios-pod-afoauth2manager.git`, branch `jasonelle`)
- AHKActionSheet
- APAddressBook (from `https://github.com/jasonelle/ios-pod-apaddressbook.git`, branch `jasonelle`)
- BBBadgeBarButtonItem
@@ -146,7 +146,7 @@ DEPENDENCIES:
- HMSegmentedControl (from `https://github.com/jasonelle/ios-pod-hmsegmentedcontrol.git`, branch `jasonelle`)
- INTULocationManager
- IQAudioRecorderController
- - JDStatusBarNotification
+ - JDStatusBarNotification (from `https://github.com/jasonelle/ios-pod-jdstatusbarnotification.git`)
- JSCoreBom (from `https://github.com/jasonelle/ios-pod-jscorebom.git`, branch `jasonelle`)
- libPhoneNumber-iOS (from `https://github.com/jasonelle/ios-pod-libphonenumber.git`, branch `jasonelle`)
- MBProgressHUD (~> 1.0)
@@ -158,7 +158,7 @@ DEPENDENCIES:
- REMenu (from `https://github.com/jasonelle/ios-pod-remenu.git`, branch `jasonelle`)
- RMDateSelectionViewController
- SBJson (from `https://github.com/jasonelle/ios-pod-sbjson.git`, branch `jasonelle`)
- - SDWebImage (from `https://github.com/jasonelle/ios-pod-sdwebimage.git`, tag `3.8.1`)
+ - SDWebImage (from `https://github.com/jasonelle/ios-pod-sdwebimage.git`, tag `3.8.3`)
- SocketRocket (from `https://github.com/jasonelle/ios-pod-socketrocket.git`, branch `jasonelle`)
- SWFrameButton
- SWTableViewCell (from `https://github.com/jasonelle/ios-pod-swtableviewcell.git`, branch `jasonelle`)
@@ -169,15 +169,13 @@ DEPENDENCIES:
- UICKeyChainStore (from `https://github.com/jasonelle/ios-pod-uickeychainstore.git`, branch `jasonelle`)
SPEC REPOS:
- https://github.com/cocoapods/specs.git:
- - AFOAuth2Manager
+ trunk:
- AHKActionSheet
- BBBadgeBarButtonItem
- DAKeyboardControl
- DHSmartScreenshot
- INTULocationManager
- IQAudioRecorderController
- - JDStatusBarNotification
- MBProgressHUD
- NSGIF
- PHFComposeBarView
@@ -193,6 +191,9 @@ EXTERNAL SOURCES:
AFNetworking:
:branch: jasonelle
:git: https://github.com/jasonelle/ios-pod-afnetworking.git
+ AFOAuth2Manager:
+ :branch: jasonelle
+ :git: https://github.com/jasonelle/ios-pod-afoauth2manager.git
APAddressBook:
:branch: jasonelle
:git: https://github.com/jasonelle/ios-pod-apaddressbook.git
@@ -213,6 +214,8 @@ EXTERNAL SOURCES:
HMSegmentedControl:
:branch: jasonelle
:git: https://github.com/jasonelle/ios-pod-hmsegmentedcontrol.git
+ JDStatusBarNotification:
+ :git: https://github.com/jasonelle/ios-pod-jdstatusbarnotification.git
JSCoreBom:
:branch: jasonelle
:git: https://github.com/jasonelle/ios-pod-jscorebom.git
@@ -236,7 +239,7 @@ EXTERNAL SOURCES:
:git: https://github.com/jasonelle/ios-pod-sbjson.git
SDWebImage:
:git: https://github.com/jasonelle/ios-pod-sdwebimage.git
- :tag: 3.8.1
+ :tag: 3.8.3
SocketRocket:
:branch: jasonelle
:git: https://github.com/jasonelle/ios-pod-socketrocket.git
@@ -255,8 +258,11 @@ EXTERNAL SOURCES:
CHECKOUT OPTIONS:
AFNetworking:
- :commit: 09ee0ddfa2c5a7cbec5cdcaee892384717ec0e43
+ :commit: 7bdce9a849da1e9a7cc29fd4c45a92115145f3d7
:git: https://github.com/jasonelle/ios-pod-afnetworking.git
+ AFOAuth2Manager:
+ :commit: eba4ec281a23e9a7b261f5d9e024cb70a8372c67
+ :git: https://github.com/jasonelle/ios-pod-afoauth2manager.git
APAddressBook:
:commit: 77888b7c4939550f84fed887945fbd22892a76c2
:git: https://github.com/jasonelle/ios-pod-apaddressbook.git
@@ -267,17 +273,20 @@ CHECKOUT OPTIONS:
:commit: 9fccb9b0de0b2eb40c6b33067b03f55f3803c153
:git: https://github.com/jasonelle/ios-pod-dtcoretext.git
DTFoundation:
- :commit: 91373daf35aafeaa94e626b9dce28a6a7276af57
+ :commit: ec0004b82ef2dbc16a93132f498e22f824eab0cb
:git: https://github.com/jasonelle/ios-pod-dtfoundation.git
FLEX:
- :commit: 226e0cd80324e7bed1d524334c2befff6b775b99
+ :commit: 2a8cdbdb84c45a89ebd48f9caad9c0420609a348
:git: https://github.com/jasonelle/ios-pod-flex.git
FreeStreamer:
- :commit: 810f241e40f2f06ccf01b9022164ab896a72b12e
+ :commit: 43be718aaaafd5dec5ec31aa713cff36af4ee414
:git: https://github.com/jasonelle/ios-pod-freestreamer.git
HMSegmentedControl:
:commit: ade9a52141a7ba47f02cef9d785cf7384ec334a6
:git: https://github.com/jasonelle/ios-pod-hmsegmentedcontrol.git
+ JDStatusBarNotification:
+ :commit: 54598de580908a542b513a7d63519511f6dd42da
+ :git: https://github.com/jasonelle/ios-pod-jdstatusbarnotification.git
JSCoreBom:
:commit: 5c2143b133248f475c30f1661a83a227c07fe138
:git: https://github.com/jasonelle/ios-pod-jscorebom.git
@@ -301,12 +310,12 @@ CHECKOUT OPTIONS:
:git: https://github.com/jasonelle/ios-pod-sbjson.git
SDWebImage:
:git: https://github.com/jasonelle/ios-pod-sdwebimage.git
- :tag: 3.8.1
+ :tag: 3.8.3
SocketRocket:
- :commit: 07b348eb13dc978c3ca84e80975b147667184b5d
+ :commit: 43d48b7a1d94a5ec8add07fc4ff892ed0ba7cc78
:git: https://github.com/jasonelle/ios-pod-socketrocket.git
SWTableViewCell:
- :commit: 1c8845e25f520c959391bd79dc6cf271e0cf9a44
+ :commit: 5cf396fe8f42dc199b157c96da29b64e9248c2e4
:git: https://github.com/jasonelle/ios-pod-swtableviewcell.git
TTTAttributedLabel:
:commit: b0f1f93d5d4368a7aaf52e848013a1b40b00dc3c
@@ -319,8 +328,8 @@ CHECKOUT OPTIONS:
:git: https://github.com/jasonelle/ios-pod-uickeychainstore.git
SPEC CHECKSUMS:
- AFNetworking: ef6403a1383e5a6fa4ca961d7d524b1898ce0a84
- AFOAuth2Manager: 0566da1be64883e339813d411229fdc9a84dab7c
+ AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
+ AFOAuth2Manager: 3166f6214e646f0bb4bb77378775a45b2290f5c5
AHKActionSheet: f0b312b5c689f72924d231c39dae140ecd5c1d46
APAddressBook: e68ecaf95f2c323b625ae585a0d25fe505291286
BBBadgeBarButtonItem: c2fbca0bff18e21b70defc9438a8a5048008e7ca
@@ -328,37 +337,37 @@ SPEC CHECKSUMS:
DAKeyboardControl: 9d9bca22a5cc3c1d3cc61e24fbd208c3e84a3d2d
DHSmartScreenshot: ee749048bd7126c4826748f98df3a4adc6ad6c78
DTCoreText: 67023bb51b26711d5f640c851f4845aea14c24c9
- DTFoundation: 25aa19bb7c6e225b1dfae195604fb8cf1da0ab4c
- FLEX: e94a8d3d52adccff1b13f7ab7e98fddd4d3ff198
+ DTFoundation: 2a31900f674de139045f4935a5656497fedda701
+ FLEX: 81096107977a09836eb440173010ceeef01105d6
FreeStreamer: 7e9c976045701ac2f7e9c14c17245203c37bf2ea
HMSegmentedControl: f514c6dad47aa3065e0ed2e8046b73efc71221b9
INTULocationManager: e5dc524611b0cd582f473bc56cc8c389adb2f338
IQAudioRecorderController: 69a67c2e0b87bd8746cbb4de175fd7a0bf820009
- JDStatusBarNotification: 21b616e7432284fea6eb0ce9eadc37c8038a5544
+ JDStatusBarNotification: 6052ca9a51ee0bb058f8dc09cf1d63554ef0f743
JSCoreBom: 787dc056d5b933617a1772ca10fb8437fed0d790
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
- MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
+ MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406
NSGIF: 442808fd1667ebac3d09046c3f6d4bdd6720cded
NSHash: 036c03781d372c288d332b512b203e7bb832e46e
OMGHTTPURLRQ: 9ac96bdf5c90a33f75836630729ea1adc312a9ed
- PHFComposeBarView: 758c73ce54be681df97223bc61543dd8828ae0d3
+ PHFComposeBarView: 6382ab846e2f4d8634273c4a78d074bc5deed07f
PHFDelegateChain: 491f9cd8a3fb8761f390ff05f74a0e168d48d285
- Reachability: a1dac0017d14de3504a13723b00f2b21f1130aea
+ Reachability: 07e976fb63651163096e04b1528d4d29a4a212aa
REMenu: 30dad195930adf6c5eeb8041e3257aa60bf9fa49
- RMActionController: b83653e7452ed51492094d65dc82151c36443c03
- RMDateSelectionViewController: 354a0f1fe40bac01054528b7a8735f2973bbd813
+ RMActionController: 535715be9f3d9eb96a838485370dcccee0dda597
+ RMDateSelectionViewController: 1157d34245e6a1dda700e8a9c44417321d181951
SBJson: 72547475887af34b161b2b9dce8c20415d8b6c68
SCSiriWaveformView: 15b9dd6f94c7536e2032b34a35c6ff74d38c7411
- SDWebImage: 35f9627a3e44b4f292a8a8ce6a531fa488239b91
- SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e
+ SDWebImage: a72e880a8fe0f7fc31efe15aaed443c074d2a80c
+ SocketRocket: 3dad623634f938c3930a9481428960d7e9fdbbf1
SWFrameButton: cd3cc4a8961e8975f4800c0f6d1d3c54c213eb87
SWTableViewCell: 2a94aadc9d47b2b611fa064566bf57948b95b579
- SZTextView: 3a81174dce0710a9ab069bc73a813e6df53b9c82
- TDOAuth: f7e356e834295b83c7b5066ee72a30f128c5f722
+ SZTextView: 094dc6acc9beec537685c545d6e3e0d4975174e1
+ TDOAuth: 1334413592cff6947d243035810425ef79bc4ced
TTTAttributedLabel: 8cffe8e127e4e82ff3af1e5386d4cd0ad000b656
TWMessageBarManager: fd84e7948ba7968a2b5d9454859135761214ec56
UICKeyChainStore: 85db518bb1d294366d15ec9b92a416c4e670518f
-PODFILE CHECKSUM: 013d012277f8fe4cfa5cfff0109e81a816fb5c9b
+PODFILE CHECKSUM: 1fc4a3f74f5694a2d347d6d9e52bd7bdb6ce2282
-COCOAPODS: 1.5.3
+COCOAPODS: 1.9.1
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFCompatibilityMacros.h b/xcode/Pods/AFNetworking/AFNetworking/AFCompatibilityMacros.h
index cffbde25..1f0ab26d 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFCompatibilityMacros.h
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFCompatibilityMacros.h
@@ -22,10 +22,16 @@
#ifndef AFCompatibilityMacros_h
#define AFCompatibilityMacros_h
+#ifdef API_AVAILABLE
+ #define AF_API_AVAILABLE(...) API_AVAILABLE(__VA_ARGS__)
+#else
+ #define AF_API_AVAILABLE(...)
+#endif // API_AVAILABLE
+
#ifdef API_UNAVAILABLE
- #define AF_API_UNAVAILABLE(x) API_UNAVAILABLE(x)
+ #define AF_API_UNAVAILABLE(...) API_UNAVAILABLE(__VA_ARGS__)
#else
- #define AF_API_UNAVAILABLE(x)
+ #define AF_API_UNAVAILABLE(...)
#endif // API_UNAVAILABLE
#if __has_warning("-Wunguarded-availability-new")
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h b/xcode/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h
index d8882fed..943fc22d 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h
@@ -25,12 +25,6 @@
#endif
#import
-#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
-#import
-#else
-#import
-#endif
-
#import "AFURLSessionManager.h"
/**
@@ -40,8 +34,6 @@
Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application.
- For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect.
-
## Methods to Override
To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:`.
@@ -138,39 +130,6 @@ NS_ASSUME_NONNULL_BEGIN
/// @name Making HTTP Requests
///---------------------------
-/**
- Creates and runs an `NSURLSessionDataTask` with a `GET` request.
-
- @param URLString The URL string used to create the request URL.
- @param parameters The parameters to be encoded according to the client request serializer.
- @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
- @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:completionHandler:
- */
-- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
- parameters:(nullable id)parameters
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
-
-
-/**
- Creates and runs an `NSURLSessionDataTask` with a `GET` request.
-
- @param URLString The URL string used to create the request URL.
- @param parameters The parameters to be encoded according to the client request serializer.
- @param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
- @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
- @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
- */
-- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
- parameters:(nullable id)parameters
- progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
-
/**
Creates and runs an `NSURLSessionDataTask` with a `GET` request.
@@ -190,21 +149,6 @@ NS_ASSUME_NONNULL_BEGIN
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
-/**
- Creates and runs an `NSURLSessionDataTask` with a `HEAD` request.
-
- @param URLString The URL string used to create the request URL.
- @param parameters The parameters to be encoded according to the client request serializer.
- @param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task.
- @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:completionHandler:
- */
-- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
- parameters:(nullable id)parameters
- success:(nullable void (^)(NSURLSessionDataTask *task))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
-
/**
Creates and runs an `NSURLSessionDataTask` with a `HEAD` request.
@@ -222,38 +166,6 @@ NS_ASSUME_NONNULL_BEGIN
success:(nullable void (^)(NSURLSessionDataTask *task))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
-/**
- Creates and runs an `NSURLSessionDataTask` with a `POST` request.
-
- @param URLString The URL string used to create the request URL.
- @param parameters The parameters to be encoded according to the client request serializer.
- @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
- @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:completionHandler:
- */
-- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
- parameters:(nullable id)parameters
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
-
-/**
- Creates and runs an `NSURLSessionDataTask` with a `POST` request.
-
- @param URLString The URL string used to create the request URL.
- @param parameters The parameters to be encoded according to the client request serializer.
- @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
- @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
- @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
- */
-- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
- parameters:(nullable id)parameters
- progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
-
/**
Creates and runs an `NSURLSessionDataTask` with a `POST` request.
@@ -273,41 +185,6 @@ NS_ASSUME_NONNULL_BEGIN
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
-/**
- Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
-
- @param URLString The URL string used to create the request URL.
- @param parameters The parameters to be encoded according to the client request serializer.
- @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
- @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
- @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:completionHandler:
- */
-- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
- parameters:(nullable id)parameters
- constructingBodyWithBlock:(nullable void (^)(id formData))block
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
-
-/**
- Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
-
- @param URLString The URL string used to create the request URL.
- @param parameters The parameters to be encoded according to the client request serializer.
- @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
- @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
- @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
- @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
- */
-- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
- parameters:(nullable id)parameters
- constructingBodyWithBlock:(nullable void (^)(id formData))block
- progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
/**
Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
@@ -329,21 +206,6 @@ NS_ASSUME_NONNULL_BEGIN
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
-/**
- Creates and runs an `NSURLSessionDataTask` with a `PUT` request.
-
- @param URLString The URL string used to create the request URL.
- @param parameters The parameters to be encoded according to the client request serializer.
- @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
- @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:completionHandler:
- */
-- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
- parameters:(nullable id)parameters
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
-
/**
Creates and runs an `NSURLSessionDataTask` with a `PUT` request.
@@ -361,21 +223,6 @@ NS_ASSUME_NONNULL_BEGIN
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
-/**
- Creates and runs an `NSURLSessionDataTask` with a `PATCH` request.
-
- @param URLString The URL string used to create the request URL.
- @param parameters The parameters to be encoded according to the client request serializer.
- @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
- @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:completionHandler:
- */
-- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
- parameters:(nullable id)parameters
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
-
/**
Creates and runs an `NSURLSessionDataTask` with a `PATCH` request.
@@ -395,35 +242,43 @@ NS_ASSUME_NONNULL_BEGIN
/**
Creates and runs an `NSURLSessionDataTask` with a `DELETE` request.
-
+
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
+ @param headers The headers appended to the default headers for this request.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
+
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
- parameters:(nullable id)parameters
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
- Creates and runs an `NSURLSessionDataTask` with a `DELETE` request.
-
+ Creates an `NSURLSessionDataTask` with a custom `HTTPMethod` request.
+
+ @param method The HTTPMethod string used to create the request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param headers The headers appended to the default headers for this request.
+ @param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
+ @param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
-
- @see -dataTaskWithRequest:completionHandler:
+
+ @see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
*/
-- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
- parameters:(nullable id)parameters
- headers:(nullable NSDictionary *)headers
- success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+- (nullable NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
+ URLString:(NSString *)URLString
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary *)headers
+ uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
+ downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
@end
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m b/xcode/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m
index 8a4f1436..b4ab5915 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m
@@ -118,30 +118,11 @@ - (void)setSecurityPolicy:(AFSecurityPolicy *)securityPolicy {
#pragma mark -
- (NSURLSessionDataTask *)GET:(NSString *)URLString
- parameters:(id)parameters
- success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
-{
-
- return [self GET:URLString parameters:parameters headers:nil progress:nil success:success failure:failure];
-}
-
-- (NSURLSessionDataTask *)GET:(NSString *)URLString
- parameters:(id)parameters
- progress:(void (^)(NSProgress * _Nonnull))downloadProgress
- success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
- failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
-{
-
- return [self GET:URLString parameters:parameters headers:nil progress:downloadProgress success:success failure:failure];
-}
-
-- (NSURLSessionDataTask *)GET:(NSString *)URLString
- parameters:(id)parameters
+ parameters:(nullable id)parameters
headers:(nullable NSDictionary *)headers
- progress:(void (^)(NSProgress * _Nonnull))downloadProgress
- success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
- failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
+ progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
+ success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
@@ -159,18 +140,10 @@ - (NSURLSessionDataTask *)GET:(NSString *)URLString
}
- (NSURLSessionDataTask *)HEAD:(NSString *)URLString
- parameters:(id)parameters
- success:(void (^)(NSURLSessionDataTask *task))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
-{
- return [self HEAD:URLString parameters:parameters headers:nil success:success failure:failure];
-}
-
-- (NSURLSessionDataTask *)HEAD:(NSString *)URLString
- parameters:(id)parameters
- headers:(NSDictionary *)headers
- success:(void (^)(NSURLSessionDataTask * _Nonnull))success
- failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary *)headers
+ success:(nullable void (^)(NSURLSessionDataTask * _Nonnull))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, __unused id responseObject) {
if (success) {
@@ -183,23 +156,6 @@ - (NSURLSessionDataTask *)HEAD:(NSString *)URLString
return dataTask;
}
-- (NSURLSessionDataTask *)POST:(NSString *)URLString
- parameters:(id)parameters
- success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
-{
- return [self POST:URLString parameters:parameters headers:nil progress:nil success:success failure:failure];
-}
-
-- (NSURLSessionDataTask *)POST:(NSString *)URLString
- parameters:(id)parameters
- progress:(void (^)(NSProgress * _Nonnull))uploadProgress
- success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
- failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
-{
- return [self POST:URLString parameters:parameters headers:nil progress:uploadProgress success:success failure:failure];
-}
-
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary *)headers
@@ -216,34 +172,15 @@ - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
+ headers:(nullable NSDictionary *)headers
constructingBodyWithBlock:(nullable void (^)(id _Nonnull))block
- success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
- failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
-{
- return [self POST:URLString parameters:parameters headers:nil constructingBodyWithBlock:block progress:nil success:success failure:failure];
-}
-
-- (NSURLSessionDataTask *)POST:(NSString *)URLString
- parameters:(id)parameters
- constructingBodyWithBlock:(void (^)(id formData))block
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
- success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
-{
- return [self POST:URLString parameters:parameters headers:nil constructingBodyWithBlock:block progress:uploadProgress success:success failure:failure];
-}
-
-- (NSURLSessionDataTask *)POST:(NSString *)URLString
- parameters:(id)parameters
- headers:(NSDictionary *)headers
- constructingBodyWithBlock:(void (^)(id _Nonnull))block
- progress:(void (^)(NSProgress * _Nonnull))uploadProgress
- success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
+ success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
for (NSString *headerField in headers.keyEnumerator) {
- [request addValue:headers[headerField] forHTTPHeaderField:headerField];
+ [request setValue:headers[headerField] forHTTPHeaderField:headerField];
}
if (serializationError) {
if (failure) {
@@ -273,18 +210,10 @@ - (NSURLSessionDataTask *)POST:(NSString *)URLString
}
- (NSURLSessionDataTask *)PUT:(NSString *)URLString
- parameters:(id)parameters
- success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
-{
- return [self PUT:URLString parameters:parameters headers:nil success:success failure:failure];
-}
-
-- (NSURLSessionDataTask *)PUT:(NSString *)URLString
- parameters:(id)parameters
- headers:(NSDictionary *)headers
- success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure];
@@ -294,18 +223,10 @@ - (NSURLSessionDataTask *)PUT:(NSString *)URLString
}
- (NSURLSessionDataTask *)PATCH:(NSString *)URLString
- parameters:(id)parameters
- success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
-{
- return [self PATCH:URLString parameters:parameters headers:nil success:success failure:failure];
-}
-
-- (NSURLSessionDataTask *)PATCH:(NSString *)URLString
- parameters:(id)parameters
- headers:(NSDictionary *)headers
- success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure];
@@ -315,18 +236,10 @@ - (NSURLSessionDataTask *)PATCH:(NSString *)URLString
}
- (NSURLSessionDataTask *)DELETE:(NSString *)URLString
- parameters:(id)parameters
- success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
-{
- return [self DELETE:URLString parameters:parameters headers:nil success:success failure:failure];
-}
-
-- (NSURLSessionDataTask *)DELETE:(NSString *)URLString
- parameters:(id)parameters
- headers:(NSDictionary *)headers
- success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
- failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary *)headers
+ success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters headers:headers uploadProgress:nil downloadProgress:nil success:success failure:failure];
@@ -335,19 +248,20 @@ - (NSURLSessionDataTask *)DELETE:(NSString *)URLString
return dataTask;
}
+
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
- parameters:(id)parameters
- headers:(NSDictionary *)headers
+ parameters:(nullable id)parameters
+ headers:(nullable NSDictionary *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
- success:(void (^)(NSURLSessionDataTask *, id))success
- failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
+ success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
+ failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
for (NSString *headerField in headers.keyEnumerator) {
- [request addValue:headers[headerField] forHTTPHeaderField:headerField];
+ [request setValue:headers[headerField] forHTTPHeaderField:headerField];
}
if (serializationError) {
if (failure) {
@@ -396,11 +310,7 @@ - (instancetype)initWithCoder:(NSCoder *)decoder {
if (!configuration) {
NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"];
if (configurationIdentifier) {
-#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100)
configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier];
-#else
- configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier];
-#endif
}
}
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h b/xcode/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h
index c005efa8..9b966a57 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h
@@ -45,10 +45,10 @@ NS_ASSUME_NONNULL_BEGIN
/**
The certificates used to evaluate server trust according to the SSL pinning mode.
-
- By default, this property is set to any (`.cer`) certificates included in the target compiling AFNetworking. Note that if you are using AFNetworking as embedded framework, no certificates will be pinned by default. Use `certificatesInBundle` to load certificates from your target, and then create a new policy by calling `policyWithPinningMode:withPinnedCertificates`.
Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches.
+
+ @see policyWithPinningMode:withPinnedCertificates:
*/
@property (nonatomic, strong, nullable) NSSet *pinnedCertificates;
@@ -90,10 +90,14 @@ NS_ASSUME_NONNULL_BEGIN
/**
Creates and returns a security policy with the specified pinning mode.
+
+ Certificates with the `.cer` extension found in the main bundle will be pinned. If you want more control over which certificates are pinned, please use `policyWithPinningMode:withPinnedCertificates:` instead.
@param pinningMode The SSL pinning mode.
@return A new security policy.
+
+ @see -policyWithPinningMode:withPinnedCertificates:
*/
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode;
@@ -104,7 +108,10 @@ NS_ASSUME_NONNULL_BEGIN
@param pinnedCertificates The certificates to pin against.
@return A new security policy.
- */
+
+ @see +certificatesInBundle:
+ @see -pinnedCertificates
+*/
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates;
///------------------------------
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m b/xcode/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m
index 3cf892e1..da199aa3 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m
@@ -60,7 +60,10 @@ static id AFPublicKeyForCertificate(NSData *certificate) {
policy = SecPolicyCreateBasicX509();
__Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
+#pragma clang diagnostic pop
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
@@ -83,7 +86,10 @@ static id AFPublicKeyForCertificate(NSData *certificate) {
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
BOOL isValid = NO;
SecTrustResultType result;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
+#pragma clang diagnostic pop
isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
@@ -115,10 +121,11 @@ static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
SecTrustRef trust;
__Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
-
SecTrustResultType result;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
__Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
-
+#pragma clang diagnostic pop
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
_out:
@@ -158,17 +165,6 @@ + (NSSet *)certificatesInBundle:(NSBundle *)bundle {
return [NSSet setWithSet:certificates];
}
-+ (NSSet *)defaultPinnedCertificates {
- static NSSet *_defaultPinnedCertificates = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSBundle *bundle = [NSBundle bundleForClass:[self class]];
- _defaultPinnedCertificates = [self certificatesInBundle:bundle];
- });
-
- return _defaultPinnedCertificates;
-}
-
+ (instancetype)defaultPolicy {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
@@ -177,7 +173,8 @@ + (instancetype)defaultPolicy {
}
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
- return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
+ NSSet *defaultPinnedCertificates = [self certificatesInBundle:[NSBundle mainBundle]];
+ return [self policyWithPinningMode:pinningMode withPinnedCertificates:defaultPinnedCertificates];
}
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
@@ -247,7 +244,7 @@ - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
- } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
+ } else if (!self.allowInvalidCertificates && !AFServerTrustIsValid(serverTrust)) {
return NO;
}
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h b/xcode/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h
index 694696b9..b17e871e 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h
@@ -216,7 +216,7 @@ forHTTPHeaderField:(NSString *)field;
@param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request.
*/
-- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
+- (void)setQueryStringSerializationWithBlock:(nullable NSString * _Nullable (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
///-------------------------------
/// @name Creating Request Objects
@@ -234,10 +234,10 @@ forHTTPHeaderField:(NSString *)field;
@return An `NSMutableURLRequest` object.
*/
-- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
- URLString:(NSString *)URLString
- parameters:(nullable id)parameters
- error:(NSError * _Nullable __autoreleasing *)error;
+- (nullable NSMutableURLRequest *)requestWithMethod:(NSString *)method
+ URLString:(NSString *)URLString
+ parameters:(nullable id)parameters
+ error:(NSError * _Nullable __autoreleasing *)error;
/**
Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m b/xcode/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m
index 75672f2b..f60b6f9d 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m
@@ -313,7 +313,7 @@ - (NSDictionary *)HTTPRequestHeaders {
- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
- dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
+ dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
});
}
@@ -335,7 +335,7 @@ - (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
}
- (void)clearAuthorizationHeader {
- dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
+ dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{
[self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
});
}
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h b/xcode/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h
index 490b721f..56a4d28a 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h
@@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
Recursively removes `NSNull` values from a JSON object.
*/
-id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions);
+FOUNDATION_EXPORT id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions);
/**
The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data.
@@ -62,8 +62,6 @@ id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions
- (instancetype)init;
-@property (nonatomic, assign) NSStringEncoding stringEncoding DEPRECATED_MSG_ATTRIBUTE("The string encoding is never used. AFHTTPResponseSerializer only validates status codes and content types but does not try to decode the received data in any way.");
-
/**
Creates and returns a serializer with default configuration.
*/
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m b/xcode/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m
index 3ddb2fa2..2715a1b3 100755
--- a/xcode/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m
@@ -271,6 +271,10 @@ - (id)responseObjectForResponse:(NSURLResponse *)response
#pragma mark - NSSecureCoding
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
@@ -488,6 +492,10 @@ - (id)responseObjectForResponse:(NSURLResponse *)response
#pragma mark - NSSecureCoding
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
@@ -704,6 +712,10 @@ - (id)responseObjectForResponse:(NSURLResponse *)response
#pragma mark - NSSecureCoding
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
@@ -790,13 +802,18 @@ - (id)responseObjectForResponse:(NSURLResponse *)response
#pragma mark - NSSecureCoding
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
- self.responseSerializers = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(responseSerializers))];
+ NSSet *classes = [NSSet setWithArray:@[[NSArray class], [AFHTTPResponseSerializer class]]];
+ self.responseSerializers = [decoder decodeObjectOfClasses:classes forKey:NSStringFromSelector(@selector(responseSerializers))];
return self;
}
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h b/xcode/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h
index 93760114..88700c39 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h
@@ -165,19 +165,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
-///---------------------------------
-/// @name Working Around System Bugs
-///---------------------------------
-
-/**
- Whether to attempt to retry creation of upload tasks for background sessions when initial call returns `nil`. `NO` by default.
-
- @bug As of iOS 7.0, there is a bug where upload tasks created for background tasks are sometimes `nil`. As a workaround, if this property is `YES`, AFNetworking will follow Apple's recommendation to try creating the task again.
-
- @see https://github.com/AFNetworking/AFNetworking/issues/1675
- */
-@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
-
///---------------------
/// @name Initialization
///---------------------
@@ -191,13 +178,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
-/**
- Invalidates the managed session, optionally canceling pending tasks.
-
- @param cancelPendingTasks Whether or not to cancel pending tasks.
- */
-- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks DEPRECATED_ATTRIBUTE;
-
/**
Invalidates the managed session, optionally canceling pending tasks and optionally resets given session.
@@ -210,15 +190,6 @@ NS_ASSUME_NONNULL_BEGIN
/// @name Running Data Tasks
///-------------------------
-/**
- Creates an `NSURLSessionDataTask` with the specified request.
-
- @param request The HTTP request for the request.
- @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
- */
-- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
- completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler DEPRECATED_ATTRIBUTE;
-
/**
Creates an `NSURLSessionDataTask` with the specified request.
@@ -344,6 +315,11 @@ NS_ASSUME_NONNULL_BEGIN
Sets a block to be executed when a connection level authentication challenge has occurred, as handled by the `NSURLSessionDelegate` method `URLSession:didReceiveChallenge:completionHandler:`.
@param block A block object to be executed when a connection level authentication challenge has occurred. The block returns the disposition of the authentication challenge, and takes three arguments: the session, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge.
+
+ @warning Implementing a session authentication challenge handler yourself totally bypasses AFNetworking's security policy defined in `AFSecurityPolicy`. Make sure you fully understand the implications before implementing a custom session authentication challenge handler. If you do not want to bypass AFNetworking's security policy, use `setTaskDidReceiveAuthenticationChallengeBlock:` instead.
+
+ @see -securityPolicy
+ @see -setTaskDidReceiveAuthenticationChallengeBlock:
*/
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
@@ -367,10 +343,23 @@ NS_ASSUME_NONNULL_BEGIN
/**
Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`.
-
- @param block A block object to be executed when a session task has received a request specific authentication challenge. The block returns the disposition of the authentication challenge, and takes four arguments: the session, the task, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge.
+
+ @param authenticationChallengeHandler A block object to be executed when a session task has received a request specific authentication challenge.
+
+ When implementing an authentication challenge handler, you should check the authentication method first (`challenge.protectionSpace.authenticationMethod `) to decide if you want to handle the authentication challenge yourself or if you want AFNetworking to handle it. If you want AFNetworking to handle the authentication challenge, just return `@(NSURLSessionAuthChallengePerformDefaultHandling)`. For example, you certainly want AFNetworking to handle certificate validation (i.e. authentication method == `NSURLAuthenticationMethodServerTrust`) as defined by the security policy. If you want to handle the challenge yourself, you have four options:
+
+ 1. Return `nil` from the authentication challenge handler. You **MUST** call the completion handler with a disposition and credentials yourself. Use this if you need to present a user interface to let the user enter their credentials.
+ 2. Return an `NSError` object from the authentication challenge handler. You **MUST NOT** call the completion handler when returning an `NSError `. The returned error will be reported in the completion handler of the task. Use this if you need to abort an authentication challenge with a specific error.
+ 3. Return an `NSURLCredential` object from the authentication challenge handler. You **MUST NOT** call the completion handler when returning an `NSURLCredential`. The returned credentials will be used to fulfil the challenge. Use this when you can get credentials without presenting a user interface.
+ 4. Return an `NSNumber` object wrapping an `NSURLSessionAuthChallengeDisposition`. Supported values are `@(NSURLSessionAuthChallengePerformDefaultHandling)`, `@(NSURLSessionAuthChallengeCancelAuthenticationChallenge)` and `@(NSURLSessionAuthChallengeRejectProtectionSpace)`. You **MUST NOT** call the completion handler when returning an `NSNumber`.
+
+ If you return anything else from the authentication challenge handler, an exception will be thrown.
+
+ For more information about how URL sessions handle the different types of authentication challenges, see [NSURLSession](https://developer.apple.com/reference/foundation/nsurlsession?language=objc) and [URL Session Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html).
+
+ @see -securityPolicy
*/
-- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
+- (void)setAuthenticationChallengeHandler:(id (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition , NSURLCredential * _Nullable)))authenticationChallengeHandler;
/**
Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
@@ -392,7 +381,7 @@ NS_ASSUME_NONNULL_BEGIN
@param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any metrics that were collected in the process of executing the task.
*/
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
-- (void)setTaskDidFinishCollectingMetricsBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * _Nullable metrics))block API_AVAILABLE(ios(10.0));
+- (void)setTaskDidFinishCollectingMetricsBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * _Nullable metrics))block AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
#endif
///-------------------------------------------
/// @name Setting Data Task Delegate Callbacks
@@ -484,6 +473,11 @@ FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification;
*/
FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification;
+/**
+ Posted when a session download task finished moving the temporary download file to a specified destination successfully.
+ */
+FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification;
+
/**
Posted when a session download task encountered an error when moving the temporary download file to a specified destination.
*/
diff --git a/xcode/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m b/xcode/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m
index 6f639ba3..c8b6810e 100644
--- a/xcode/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m
+++ b/xcode/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m
@@ -22,35 +22,6 @@
#import "AFURLSessionManager.h"
#import
-#ifndef NSFoundationVersionNumber_iOS_8_0
-#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11
-#else
-#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0
-#endif
-
-static dispatch_queue_t url_session_manager_creation_queue() {
- static dispatch_queue_t af_url_session_manager_creation_queue;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
- });
-
- return af_url_session_manager_creation_queue;
-}
-
-static void url_session_manager_create_task_safely(dispatch_block_t _Nonnull block) {
- if (block != NULL) {
- if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
- // Fix of bug
- // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
- // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
- dispatch_sync(url_session_manager_creation_queue(), block);
- } else {
- block();
- }
- }
-}
-
static dispatch_queue_t url_session_manager_processing_queue() {
static dispatch_queue_t af_url_session_manager_processing_queue;
static dispatch_once_t onceToken;
@@ -75,6 +46,7 @@ static dispatch_group_t url_session_manager_completion_group() {
NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
+NSString * const AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification = @"com.alamofire.networking.session.download.file-manager-succeed";
NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";
NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse";
@@ -86,21 +58,19 @@ static dispatch_group_t url_session_manager_completion_group() {
static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
-static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3;
-
typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
+typedef id (^AFURLSessionTaskAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential));
typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session);
typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task);
typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error);
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
-API_AVAILABLE(ios(10.0))
-typedef void (^AFURLSessionTaskDidFinishCollectingMetricsBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * metrics);
+typedef void (^AFURLSessionTaskDidFinishCollectingMetricsBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLSessionTaskMetrics * metrics) AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
#endif
typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response);
@@ -115,10 +85,8 @@ static dispatch_group_t url_session_manager_completion_group() {
typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
-
#pragma mark -
-
@interface AFURLSessionManagerTaskDelegate : NSObject
- (instancetype)initWithTask:(NSURLSessionTask *)task;
@property (nonatomic, weak) AFURLSessionManager *manager;
@@ -127,10 +95,7 @@ - (instancetype)initWithTask:(NSURLSessionTask *)task;
@property (nonatomic, strong) NSProgress *downloadProgress;
@property (nonatomic, copy) NSURL *downloadFileURL;
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunguarded-availability"
-@property (nonatomic, strong) NSURLSessionTaskMetrics *sessionTaskMetrics;
-#pragma clang diagnosric pop
+@property (nonatomic, strong) NSURLSessionTaskMetrics *sessionTaskMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
#endif
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
@@ -163,7 +128,7 @@ - (instancetype)initWithTask:(NSURLSessionTask *)task {
[weakTask suspend];
};
#if AF_CAN_USE_AT_AVAILABLE
- if (@available(iOS 9, macOS 10.11, *))
+ if (@available(macOS 10.11, *))
#else
if ([progress respondsToSelector:@selector(setResumingHandler:)])
#endif
@@ -201,17 +166,20 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
}
}
+static const void * const AuthenticationChallengeErrorKey = &AuthenticationChallengeErrorKey;
+
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
+ error = objc_getAssociatedObject(task, AuthenticationChallengeErrorKey) ?: error;
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
- __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #2672
@@ -281,7 +249,7 @@ - (void)URLSession:(__unused NSURLSession *)session
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
-didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(ios(10.0)){
+didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) {
self.sessionTaskMetrics = metrics;
}
#endif
@@ -339,6 +307,8 @@ - (void)URLSession:(NSURLSession *)session
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
+ } else {
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
}
}
}
@@ -486,12 +456,12 @@ @interface AFURLSessionManager ()
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos);
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
-@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
+@property (readwrite, nonatomic, copy) AFURLSessionTaskAuthenticationChallengeBlock authenticationChallengeHandler;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
-@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics;
+@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
#endif
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@@ -536,20 +506,17 @@ - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)config
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
- __weak typeof(self) weakSelf = self;
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
-
- __strong typeof(weakSelf) strongSelf = weakSelf;
for (NSURLSessionDataTask *task in dataTasks) {
- [strongSelf addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
+ [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
- [strongSelf addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
+ [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
- [strongSelf addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
+ [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
@@ -730,10 +697,6 @@ - (NSArray *)downloadTasks {
#pragma mark -
-- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks {
- [self invalidateSessionCancelingTasks:cancelPendingTasks resetSession:NO];
-}
-
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks resetSession:(BOOL)resetSession {
if (cancelPendingTasks) {
[self.session invalidateAndCancel];
@@ -766,21 +729,12 @@ - (void)removeNotificationObserverForTask:(NSURLSessionTask *)task {
#pragma mark -
-- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
- completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
-{
- return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
-}
-
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
- __block NSURLSessionDataTask *dataTask = nil;
- url_session_manager_create_task_safely(^{
- dataTask = [self.session dataTaskWithRequest:request];
- });
+ NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
@@ -794,17 +748,7 @@ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
- __block NSURLSessionUploadTask *uploadTask = nil;
- url_session_manager_create_task_safely(^{
- uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
-
- // uploadTask may be nil on iOS7 because uploadTaskWithRequest:fromFile: may return nil despite being documented as nonnull (https://devforums.apple.com/message/926113#926113)
- if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
- for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
- uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
- }
- }
- });
+ NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
if (uploadTask) {
[self addDelegateForUploadTask:uploadTask
@@ -820,11 +764,8 @@ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
- __block NSURLSessionUploadTask *uploadTask = nil;
- url_session_manager_create_task_safely(^{
- uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
- });
-
+ NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
+
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
@@ -834,10 +775,7 @@ - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)reques
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
- __block NSURLSessionUploadTask *uploadTask = nil;
- url_session_manager_create_task_safely(^{
- uploadTask = [self.session uploadTaskWithStreamedRequest:request];
- });
+ NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
@@ -851,11 +789,8 @@ - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
- __block NSURLSessionDownloadTask *downloadTask = nil;
- url_session_manager_create_task_safely(^{
- downloadTask = [self.session downloadTaskWithRequest:request];
- });
-
+ NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
+
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
@@ -866,10 +801,7 @@ - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
- __block NSURLSessionDownloadTask *downloadTask = nil;
- url_session_manager_create_task_safely(^{
- downloadTask = [self.session downloadTaskWithResumeData:resumeData];
- });
+ NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData];
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
@@ -911,10 +843,6 @@ - (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession
self.taskWillPerformHTTPRedirection = block;
}
-- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
- self.taskDidReceiveAuthenticationChallenge = block;
-}
-
- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block {
self.taskDidSendBodyData = block;
}
@@ -924,7 +852,7 @@ - (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTas
}
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
-- (void)setTaskDidFinishCollectingMetricsBlock:(void (^)(NSURLSession * _Nonnull, NSURLSessionTask * _Nonnull, NSURLSessionTaskMetrics * _Nullable))block {
+- (void)setTaskDidFinishCollectingMetricsBlock:(void (^)(NSURLSession * _Nonnull, NSURLSessionTask * _Nonnull, NSURLSessionTaskMetrics * _Nullable))block AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10)) {
self.taskDidFinishCollectingMetrics = block;
}
#endif
@@ -968,7 +896,9 @@ - (NSString *)description {
}
- (BOOL)respondsToSelector:(SEL)selector {
- if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
+ if (selector == @selector(URLSession:didReceiveChallenge:completionHandler:)) {
+ return self.sessionDidReceiveAuthenticationChallenge != nil;
+ } else if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
return self.taskWillPerformHTTPRedirection != nil;
} else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
return self.dataTaskDidReceiveResponse != nil;
@@ -1000,27 +930,10 @@ - (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
- NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
- __block NSURLCredential *credential = nil;
+ NSAssert(self.sessionDidReceiveAuthenticationChallenge != nil, @"`respondsToSelector:` implementation forces `URLSession:didReceiveChallenge:completionHandler:` to be called only if `self.sessionDidReceiveAuthenticationChallenge` is not nil");
- if (self.sessionDidReceiveAuthenticationChallenge) {
- disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
- } else {
- if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
- if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
- credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
- if (credential) {
- disposition = NSURLSessionAuthChallengeUseCredential;
- } else {
- disposition = NSURLSessionAuthChallengePerformDefaultHandling;
- }
- } else {
- disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
- }
- } else {
- disposition = NSURLSessionAuthChallengePerformDefaultHandling;
- }
- }
+ NSURLCredential *credential = nil;
+ NSURLSessionAuthChallengeDisposition disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
if (completionHandler) {
completionHandler(disposition, credential);
@@ -1051,21 +964,40 @@ - (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
+ BOOL evaluateServerTrust = NO;
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
- __block NSURLCredential *credential = nil;
+ NSURLCredential *credential = nil;
- if (self.taskDidReceiveAuthenticationChallenge) {
- disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
+ if (self.authenticationChallengeHandler) {
+ id result = self.authenticationChallengeHandler(session, task, challenge, completionHandler);
+ if (result == nil) {
+ return;
+ } else if ([result isKindOfClass:NSError.class]) {
+ objc_setAssociatedObject(task, AuthenticationChallengeErrorKey, result, OBJC_ASSOCIATION_RETAIN);
+ disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
+ } else if ([result isKindOfClass:NSURLCredential.class]) {
+ credential = result;
+ disposition = NSURLSessionAuthChallengeUseCredential;
+ } else if ([result isKindOfClass:NSNumber.class]) {
+ disposition = [result integerValue];
+ NSAssert(disposition == NSURLSessionAuthChallengePerformDefaultHandling || disposition == NSURLSessionAuthChallengeCancelAuthenticationChallenge || disposition == NSURLSessionAuthChallengeRejectProtectionSpace, @"");
+ evaluateServerTrust = disposition == NSURLSessionAuthChallengePerformDefaultHandling && [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
+ } else {
+ @throw [NSException exceptionWithName:@"Invalid Return Value" reason:@"The return value from the authentication challenge handler must be nil, an NSError, an NSURLCredential or an NSNumber." userInfo:nil];
+ }
} else {
- if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
- if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
- disposition = NSURLSessionAuthChallengeUseCredential;
- credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
- } else {
- disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
- }
+ evaluateServerTrust = [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
+ }
+
+ if (evaluateServerTrust) {
+ if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
+ disposition = NSURLSessionAuthChallengeUseCredential;
+ credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
- disposition = NSURLSessionAuthChallengePerformDefaultHandling;
+ objc_setAssociatedObject(task, AuthenticationChallengeErrorKey,
+ [self serverTrustErrorForServerTrust:challenge.protectionSpace.serverTrust url:task.currentRequest.URL],
+ OBJC_ASSOCIATION_RETAIN);
+ disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
}
@@ -1074,6 +1006,31 @@ - (void)URLSession:(NSURLSession *)session
}
}
+- (nonnull NSError *)serverTrustErrorForServerTrust:(nullable SecTrustRef)serverTrust url:(nullable NSURL *)url
+{
+ NSBundle *CFNetworkBundle = [NSBundle bundleWithIdentifier:@"com.apple.CFNetwork"];
+ NSString *defaultValue = @"The certificate for this server is invalid. You might be connecting to a server that is pretending to be “%@” which could put your confidential information at risk.";
+ NSString *descriptionFormat = NSLocalizedStringWithDefaultValue(@"Err-1202.w", nil, CFNetworkBundle, defaultValue, @"") ?: defaultValue;
+ NSString *localizedDescription = [descriptionFormat componentsSeparatedByString:@"%@"].count <= 2 ? [NSString localizedStringWithFormat:descriptionFormat, url.host] : descriptionFormat;
+ NSMutableDictionary *userInfo = [@{
+ NSLocalizedDescriptionKey: localizedDescription
+ } mutableCopy];
+
+ if (serverTrust) {
+ userInfo[NSURLErrorFailingURLPeerTrustErrorKey] = (__bridge id)serverTrust;
+ }
+
+ if (url) {
+ userInfo[NSURLErrorFailingURLErrorKey] = url;
+
+ if (url.absoluteString) {
+ userInfo[NSURLErrorFailingURLStringErrorKey] = url.absoluteString;
+ }
+ }
+
+ return [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorServerCertificateUntrusted userInfo:userInfo];
+}
+
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
@@ -1138,8 +1095,8 @@ - (void)URLSession:(NSURLSession *)session
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
-didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics
-API_AVAILABLE(ios(10.0)){
+didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10))
+{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// Metrics may fire after URLSession:task:didCompleteWithError: is called, delegate may be nil
if (delegate) {
@@ -1239,6 +1196,8 @@ - (void)URLSession:(NSURLSession *)session
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
+ } else {
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
}
return;
diff --git a/xcode/Pods/AFNetworking/LICENSE b/xcode/Pods/AFNetworking/LICENSE
index d7076267..f611f42f 100644
--- a/xcode/Pods/AFNetworking/LICENSE
+++ b/xcode/Pods/AFNetworking/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2011-2016 Alamofire Software Foundation (http://alamofire.org/)
+Copyright (c) 2011-2020 Alamofire Software Foundation (http://alamofire.org/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/xcode/Pods/AFNetworking/README.md b/xcode/Pods/AFNetworking/README.md
index 023eedc5..d193dfe8 100644
--- a/xcode/Pods/AFNetworking/README.md
+++ b/xcode/Pods/AFNetworking/README.md
@@ -2,8 +2,7 @@
-[![Build Status](https://travis-ci.org/AFNetworking/AFNetworking.svg)](https://travis-ci.org/AFNetworking/AFNetworking)
-[![codecov.io](https://codecov.io/github/AFNetworking/AFNetworking/coverage.svg?branch=master)](https://codecov.io/github/AFNetworking/AFNetworking?branch=master)
+[![Build Status](https://github.com/AFNetworking/AFNetworking/workflows/AFNetworking%20CI/badge.svg?branch=master)](https://github.com/AFNetworking/AFNetworking/actions)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/AFNetworking.svg)](https://img.shields.io/cocoapods/v/AFNetworking.svg)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Platform](https://img.shields.io/cocoapods/p/AFNetworking.svg?style=flat)](http://cocoadocs.org/docsets/AFNetworking)
@@ -13,14 +12,10 @@ AFNetworking is a delightful networking library for iOS, macOS, watchOS, and tvO
Perhaps the most important feature of all, however, is the amazing community of developers who use and contribute to AFNetworking every day. AFNetworking powers some of the most popular and critically-acclaimed apps on the iPhone, iPad, and Mac.
-Choose AFNetworking for your next project, or migrate over your existing projects—you'll be happy you did!
-
## How To Get Started
- [Download AFNetworking](https://github.com/AFNetworking/AFNetworking/archive/master.zip) and try out the included Mac and iPhone example apps
- Read the ["Getting Started" guide](https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking), [FAQ](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ), or [other articles on the Wiki](https://github.com/AFNetworking/AFNetworking/wiki)
-- Check out the [documentation](http://cocoadocs.org/docsets/AFNetworking/) for a comprehensive look at all of the APIs available in AFNetworking
-- Read the [AFNetworking 3.0 Migration Guide](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-3.0-Migration-Guide) for an overview of the architectural changes from 2.0.
## Communication
@@ -35,56 +30,37 @@ AFNetworking supports multiple methods for installing the library in a project.
## Installation with CocoaPods
-[CocoaPods](http://cocoapods.org) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like AFNetworking in your projects. See the ["Getting Started" guide for more information](https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking). You can install it with the following command:
-
-```bash
-$ gem install cocoapods
-```
-
-> CocoaPods 0.39.0+ is required to build AFNetworking 3.0.0+.
-
-#### Podfile
-
To integrate AFNetworking into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
-source 'https://github.com/CocoaPods/Specs.git'
-platform :ios, '8.0'
-
-target 'TargetName' do
-pod 'AFNetworking', '~> 3.0'
-end
+pod 'AFNetworking', '~> 4.0'
```
-Then, run the following command:
+### Installation with Swift Package Manager
-```bash
-$ pod install
-```
-
-### Installation with Carthage
+Once you have your Swift package set up, adding AFNetworking as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
-[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
+```swift
+dependencies: [
+ .package(url: "https://github.com/AFNetworking/AFNetworking.git", .upToNextMajor(from: "4.0.0"))
+]
+```
-You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
+> Note: AFNetworking's Swift package does not include it's UIKit extensions.
-```bash
-$ brew update
-$ brew install carthage
-```
+### Installation with Carthage
-To integrate AFNetworking into your Xcode project using Carthage, specify it in your `Cartfile`:
+[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate AFNetworking, add the following to your `Cartfile`.
```ogdl
-github "AFNetworking/AFNetworking" ~> 3.0
+github "AFNetworking/AFNetworking" ~> 4.0
```
-Run `carthage` to build the framework and drag the built `AFNetworking.framework` into your Xcode project.
-
## Requirements
| AFNetworking Version | Minimum iOS Target | Minimum macOS Target | Minimum watchOS Target | Minimum tvOS Target | Notes |
|:--------------------:|:---------------------------:|:----------------------------:|:----------------------------:|:----------------------------:|:-------------------------------------------------------------------------:|
+| 4.x | iOS 9 | macOS 10.10 | watchOS 2.0 | tvOS 9.0 | Xcode 11+ is required. |
| 3.x | iOS 7 | OS X 10.9 | watchOS 2.0 | tvOS 9.0 | Xcode 7+ is required. `NSURLConnectionOperation` support has been removed. |
| 2.6 -> 2.6.3 | iOS 7 | OS X 10.9 | watchOS 2.0 | n/a | Xcode 7+ is required. |
| 2.0 -> 2.5.4 | iOS 6 | OS X 10.8 | n/a | n/a | Xcode 5+ is required. `NSURLSession` subspec requires iOS 7 or OS X 10.9. |
diff --git a/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h b/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h
index 7e25e373..3bf5a320 100644
--- a/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h
+++ b/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h
@@ -67,7 +67,7 @@ typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
/**
Defines the order prioritization of incoming download requests being inserted into the queue. `AFImageDownloadPrioritizationFIFO` by default.
*/
-@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritizaton;
+@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritization;
/**
The shared default instance of `AFImageDownloader` initialized with default values.
diff --git a/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m b/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m
index 74bbab0c..008a7828 100644
--- a/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m
+++ b/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m
@@ -109,20 +109,24 @@ @interface AFImageDownloader ()
@implementation AFImageDownloader
+ (NSURLCache *)defaultURLCache {
+ NSUInteger memoryCapacity = 20 * 1024 * 1024; // 20MB
+ NSUInteger diskCapacity = 150 * 1024 * 1024; // 150MB
+ NSURL *cacheURL = [[[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
+ inDomain:NSUserDomainMask
+ appropriateForURL:nil
+ create:YES
+ error:nil]
+ URLByAppendingPathComponent:@"com.alamofire.imagedownloader"];
- // It's been discovered that a crash will occur on certain versions
- // of iOS if you customize the cache.
- //
- // More info can be found here: https://devforums.apple.com/message/1102182#1102182
- //
- // When iOS 7 support is dropped, this should be modified to use
- // NSProcessInfo methods instead.
- if ([[[UIDevice currentDevice] systemVersion] compare:@"8.2" options:NSNumericSearch] == NSOrderedAscending) {
- return [NSURLCache sharedURLCache];
- }
- return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
- diskCapacity:150 * 1024 * 1024
- diskPath:@"com.alamofire.imagedownloader"];
+#if TARGET_OS_MACCATALYST
+ return [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity
+ diskCapacity:diskCapacity
+ directoryURL:cacheURL];
+#else
+ return [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity
+ diskCapacity:diskCapacity
+ diskPath:[cacheURL path]];
+#endif
}
+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration {
@@ -163,7 +167,7 @@ - (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager
if (self = [super init]) {
self.sessionManager = sessionManager;
- self.downloadPrioritizaton = downloadPrioritization;
+ self.downloadPrioritization = downloadPrioritization;
self.maximumActiveDownloads = maximumActiveDownloads;
self.imageCache = imageCache;
@@ -333,7 +337,7 @@ - (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloa
}
}
- if (mergedTask.responseHandlers.count == 0 && mergedTask.task.state == NSURLSessionTaskStateSuspended) {
+ if (mergedTask.responseHandlers.count == 0) {
[mergedTask.task cancel];
[self removeMergedTaskWithURLIdentifier:URLIdentifier];
}
@@ -383,7 +387,7 @@ - (void)startMergedTask:(AFImageDownloaderMergedTask *)mergedTask {
}
- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask {
- switch (self.downloadPrioritizaton) {
+ switch (self.downloadPrioritization) {
case AFImageDownloadPrioritizationFIFO:
[self.queuedMergedTasks addObject:mergedTask];
break;
diff --git a/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m b/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m
index e6f9b65e..8cb5677e 100644
--- a/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m
+++ b/xcode/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m
@@ -109,11 +109,9 @@ - (BOOL)isNetworkActivityOccurring {
- (void)setNetworkActivityIndicatorVisible:(BOOL)networkActivityIndicatorVisible {
if (_networkActivityIndicatorVisible != networkActivityIndicatorVisible) {
- [self willChangeValueForKey:@"networkActivityIndicatorVisible"];
@synchronized(self) {
- _networkActivityIndicatorVisible = networkActivityIndicatorVisible;
+ _networkActivityIndicatorVisible = networkActivityIndicatorVisible;
}
- [self didChangeValueForKey:@"networkActivityIndicatorVisible"];
if (self.networkActivityActionBlock) {
self.networkActivityActionBlock(networkActivityIndicatorVisible);
} else {
@@ -122,35 +120,20 @@ - (void)setNetworkActivityIndicatorVisible:(BOOL)networkActivityIndicatorVisible
}
}
-- (void)setActivityCount:(NSInteger)activityCount {
- @synchronized(self) {
- _activityCount = activityCount;
- }
-
- dispatch_async(dispatch_get_main_queue(), ^{
- [self updateCurrentStateForNetworkActivityChange];
- });
-}
- (void)incrementActivityCount {
- [self willChangeValueForKey:@"activityCount"];
- @synchronized(self) {
- _activityCount++;
- }
- [self didChangeValueForKey:@"activityCount"];
-
+ @synchronized(self) {
+ self.activityCount++;
+ }
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCurrentStateForNetworkActivityChange];
});
}
- (void)decrementActivityCount {
- [self willChangeValueForKey:@"activityCount"];
- @synchronized(self) {
- _activityCount = MAX(_activityCount - 1, 0);
- }
- [self didChangeValueForKey:@"activityCount"];
-
+ @synchronized(self) {
+ self.activityCount = MAX(_activityCount - 1, 0);
+ }
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCurrentStateForNetworkActivityChange];
});
@@ -172,7 +155,6 @@ - (void)networkRequestDidFinish:(NSNotification *)notification {
- (void)setCurrentState:(AFNetworkActivityManagerState)currentState {
@synchronized(self) {
if (_currentState != currentState) {
- [self willChangeValueForKey:@"currentState"];
_currentState = currentState;
switch (currentState) {
case AFNetworkActivityManagerStateNotActive:
@@ -191,9 +173,7 @@ - (void)setCurrentState:(AFNetworkActivityManagerState)currentState {
[self startCompletionDelayTimer];
break;
}
- [self didChangeValueForKey:@"currentState"];
}
-
}
}
diff --git a/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m b/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m
index 6ac02261..03aaf2a8 100644
--- a/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m
+++ b/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m
@@ -103,11 +103,11 @@ @implementation UIButton (AFNetworking)
+ (AFImageDownloader *)sharedImageDownloader {
- return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
+ return objc_getAssociatedObject([UIButton class], @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
}
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
- objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ objc_setAssociatedObject([UIButton class], @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark -
diff --git a/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m b/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m
index d1d615fb..8ae49509 100644
--- a/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m
+++ b/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m
@@ -48,11 +48,11 @@ - (void)af_setActiveImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownload
@implementation UIImageView (AFNetworking)
+ (AFImageDownloader *)sharedImageDownloader {
- return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
+ return objc_getAssociatedObject([UIImageView class], @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
}
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
- objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+ objc_setAssociatedObject([UIImageView class], @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark -
diff --git a/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h b/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h
index 10167edc..ecb96894 100644
--- a/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h
+++ b/xcode/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h
@@ -20,22 +20,25 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#if TARGET_OS_IOS || TARGET_OS_TV
-#import
+#import
#ifndef _UIKIT_AFNETWORKING_
#define _UIKIT_AFNETWORKING_
-#if TARGET_OS_IOS
+#if TARGET_OS_IOS || TARGET_OS_TV
#import "AFAutoPurgingImageCache.h"
#import "AFImageDownloader.h"
- #import "AFNetworkActivityIndicatorManager.h"
- #import "UIRefreshControl+AFNetworking.h"
-#endif
-
#import "UIActivityIndicatorView+AFNetworking.h"
#import "UIButton+AFNetworking.h"
+ #import "UIImage+AFNetworking.h"
#import "UIImageView+AFNetworking.h"
#import "UIProgressView+AFNetworking.h"
-#endif /* _UIKIT_AFNETWORKING_ */
#endif
+
+#if TARGET_OS_IOS
+ #import "AFNetworkActivityIndicatorManager.h"
+ #import "UIRefreshControl+AFNetworking.h"
+ #import "WKWebView+AFNetworking.h"
+#endif
+
+#endif /* _UIKIT_AFNETWORKING_ */
diff --git a/xcode/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.h b/xcode/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.h
new file mode 100644
index 00000000..680fedff
--- /dev/null
+++ b/xcode/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.h
@@ -0,0 +1,80 @@
+// WkWebView+AFNetworking.h
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import
+
+#import
+
+#if TARGET_OS_IOS
+
+#import
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class AFHTTPSessionManager;
+
+@interface WKWebView (AFNetworking)
+
+/**
+ The session manager used to download all request
+ */
+@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;
+
+/**
+ Asynchronously loads the specified request.
+
+ @param request A URL request identifying the location of the content to load. This must not be `nil`.
+ @param navigation The WKNavigation object that containts information for tracking the loading progress of a webpage. This must not be `nil`.
+ @param progress A progress object monitoring the current download progress.
+ @param success A block object to be executed when the request finishes loading successfully. This block returns the HTML string to be loaded by the web view, and takes two arguments: the response, and the response string.
+ @param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred.
+ */
+- (void)loadRequest:(NSURLRequest *)request
+ navigation:(WKNavigation * _Nonnull)navigation
+ progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
+ success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success
+ failure:(nullable void (^)(NSError *error))failure;
+
+/**
+ Asynchronously loads the data associated with a particular request with a specified MIME type and text encoding.
+
+ @param request A URL request identifying the location of the content to load. This must not be `nil`.
+ @param navigation The WKNavigation object that containts information for tracking the loading progress of a webpage. This must not be `nil`.
+ @param MIMEType The MIME type of the content. Defaults to the content type of the response if not specified.
+ @param textEncodingName The IANA encoding name, as in `utf-8` or `utf-16`. Defaults to the response text encoding if not specified.
+ @param progress A progress object monitoring the current download progress.
+ @param success A block object to be executed when the request finishes loading successfully. This block returns the data to be loaded by the web view and takes two arguments: the response, and the downloaded data.
+ @param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred.
+ */
+- (void)loadRequest:(NSURLRequest *)request
+ navigation:(WKNavigation * _Nonnull)navigation
+ MIMEType:(nullable NSString *)MIMEType
+ textEncodingName:(nullable NSString *)textEncodingName
+ progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
+ success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
+ failure:(nullable void (^)(NSError *error))failure;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif
diff --git a/xcode/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.m b/xcode/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.m
new file mode 100644
index 00000000..6eca3c3a
--- /dev/null
+++ b/xcode/Pods/AFNetworking/UIKit+AFNetworking/WKWebView+AFNetworking.m
@@ -0,0 +1,154 @@
+// WkWebView+AFNetworking.m
+// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "WKWebView+AFNetworking.h"
+
+#import
+
+#if TARGET_OS_IOS
+
+#import "AFHTTPSessionManager.h"
+#import "AFURLResponseSerialization.h"
+#import "AFURLRequestSerialization.h"
+
+@interface WKWebView (_AFNetworking)
+@property (readwrite, nonatomic, strong, setter = af_setURLSessionTask:) NSURLSessionDataTask *af_URLSessionTask;
+@end
+
+@implementation WKWebView (_AFNetworking)
+
+- (NSURLSessionDataTask *)af_URLSessionTask {
+ return (NSURLSessionDataTask *)objc_getAssociatedObject(self, @selector(af_URLSessionTask));
+}
+
+- (void)af_setURLSessionTask:(NSURLSessionDataTask *)af_URLSessionTask {
+ objc_setAssociatedObject(self, @selector(af_URLSessionTask), af_URLSessionTask, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
+
+#pragma mark -
+
+@implementation WKWebView (AFNetworking)
+
+- (AFHTTPSessionManager *)sessionManager {
+ static AFHTTPSessionManager *_af_defaultHTTPSessionManager = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ _af_defaultHTTPSessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
+ _af_defaultHTTPSessionManager.requestSerializer = [AFHTTPRequestSerializer serializer];
+ _af_defaultHTTPSessionManager.responseSerializer = [AFHTTPResponseSerializer serializer];
+ });
+
+ return objc_getAssociatedObject(self, @selector(sessionManager)) ?: _af_defaultHTTPSessionManager;
+}
+
+- (void)setSessionManager:(AFHTTPSessionManager *)sessionManager {
+ objc_setAssociatedObject(self, @selector(sessionManager), sessionManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (AFHTTPResponseSerializer *)responseSerializer {
+ static AFHTTPResponseSerializer *_af_defaultResponseSerializer = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ _af_defaultResponseSerializer = [AFHTTPResponseSerializer serializer];
+ });
+
+ return objc_getAssociatedObject(self, @selector(responseSerializer)) ?: _af_defaultResponseSerializer;
+}
+
+- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer {
+ objc_setAssociatedObject(self, @selector(responseSerializer), responseSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+#pragma mark -
+
+- (void)loadRequest:(NSURLRequest *)request
+ navigation:(WKNavigation * _Nonnull)navigation
+ progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
+ success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success
+ failure:(nullable void (^)(NSError *error))failure {
+ [self loadRequest:request navigation:navigation MIMEType:nil textEncodingName:nil progress:progress success:^NSData * _Nonnull(NSHTTPURLResponse * _Nonnull response, NSData * _Nonnull data) {
+ NSStringEncoding stringEncoding = NSUTF8StringEncoding;
+ if (response.textEncodingName) {
+ CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
+ if (encoding != kCFStringEncodingInvalidId) {
+ stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding);
+ }
+ }
+
+ NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding];
+ if (success) {
+ string = success(response, string);
+ }
+
+ return [string dataUsingEncoding:stringEncoding];
+ } failure:failure];
+}
+
+- (void)loadRequest:(NSURLRequest *)request
+ navigation:(WKNavigation * _Nonnull)navigation
+ MIMEType:(nullable NSString *)MIMEType
+ textEncodingName:(nullable NSString *)textEncodingName
+ progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
+ success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
+ failure:(nullable void (^)(NSError *error))failure {
+ NSParameterAssert(request);
+
+ if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) {
+ [self.af_URLSessionTask cancel];
+ }
+ self.af_URLSessionTask = nil;
+
+ __weak __typeof(self)weakSelf = self;
+ __block NSURLSessionDataTask *dataTask;
+ __strong __typeof(weakSelf) strongSelf = weakSelf;
+ __strong __typeof(weakSelf.navigationDelegate) strongSelfDelegate = strongSelf.navigationDelegate;
+ dataTask = [self.sessionManager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
+ if (error) {
+ if (failure) {
+ failure(error);
+ }
+ } else {
+ if (success) {
+ success((NSHTTPURLResponse *)response, responseObject);
+ }
+ [strongSelf loadData:responseObject MIMEType:MIMEType characterEncodingName:textEncodingName baseURL:[dataTask.currentRequest URL]];
+
+ if ([strongSelfDelegate respondsToSelector:@selector(webView:didFinishNavigation:)]) {
+ [strongSelfDelegate webView:strongSelf didFinishNavigation:navigation];
+ }
+ }
+ }];
+ self.af_URLSessionTask = dataTask;
+ if (progress != nil) {
+ *progress = [self.sessionManager downloadProgressForTask:dataTask];
+ }
+ [self.af_URLSessionTask resume];
+
+ if ([strongSelfDelegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) {
+ [strongSelfDelegate webView:self didStartProvisionalNavigation:navigation];
+ }
+}
+
+@end
+
+#endif
diff --git a/xcode/Pods/AFOAuth2Manager/AFOAuth2Manager/AFOAuth2Manager.m b/xcode/Pods/AFOAuth2Manager/AFOAuth2Manager/AFOAuth2Manager.m
index 88d83f11..6ad3ab61 100644
--- a/xcode/Pods/AFOAuth2Manager/AFOAuth2Manager/AFOAuth2Manager.m
+++ b/xcode/Pods/AFOAuth2Manager/AFOAuth2Manager/AFOAuth2Manager.m
@@ -222,7 +222,10 @@ - (NSURLSessionTask *)authenticateUsingOAuthWithURLString:(NSString *)URLString
parameters = [NSDictionary dictionaryWithDictionary:mutableParameters];
NSURLSessionTask *task;
- task = [self POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
+
+ task = [self POST:URLString parameters:parameters headers:@{} progress:^(NSProgress * _Nonnull uploadProgress) {
+
+ } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (!responseObject) {
if (failure) {
failure(nil);
@@ -262,7 +265,6 @@ - (NSURLSessionTask *)authenticateUsingOAuthWithURLString:(NSString *)URLString
if (success) {
success(credential);
}
-
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (failure) {
failure(error);
diff --git a/xcode/Pods/DTFoundation/Core/Source/DTUTI/NSString+DTUTI.m b/xcode/Pods/DTFoundation/Core/Source/DTUTI/NSString+DTUTI.m
index 7a4695f4..db7e3c71 100644
--- a/xcode/Pods/DTFoundation/Core/Source/DTUTI/NSString+DTUTI.m
+++ b/xcode/Pods/DTFoundation/Core/Source/DTUTI/NSString+DTUTI.m
@@ -9,7 +9,7 @@
#import "NSString+DTUTI.h"
#if TARGET_OS_IPHONE
-#import
+#import
#endif
@implementation NSString (DTUTI)
diff --git a/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXFilteringTableViewController.h b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXFilteringTableViewController.h
new file mode 100644
index 00000000..492d60da
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXFilteringTableViewController.h
@@ -0,0 +1,89 @@
+//
+// FLEXFilteringTableViewController.h
+// FLEX
+//
+// Created by Tanner on 3/9/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableViewController.h"
+
+#pragma mark - FLEXTableViewFiltering
+@protocol FLEXTableViewFiltering
+
+/// An array of visible, "filtered" sections. For example,
+/// if you have 3 sections in \c allSections and the user searches
+/// for something that matches rows in only one section, then
+/// this property would only contain that on matching section.
+@property (nonatomic, copy) NSArray *sections;
+/// An array of all possible sections. Empty sections are to be removed
+/// and the resulting array stored in the \c section property. Setting
+/// this property should immediately set \c sections to \c nonemptySections
+///
+/// Do not manually initialize this property, it will be
+/// initialized for you using the result of \c makeSections.
+@property (nonatomic, copy) NSArray *allSections;
+
+/// This computed property should filter \c allSections for assignment to \c sections
+@property (nonatomic, readonly) NSArray *nonemptySections;
+
+/// This should be able to re-initialize \c allSections
+- (NSArray *)makeSections;
+
+@end
+
+
+#pragma mark - FLEXFilteringTableViewController
+/// A table view which implements \c UITableView* methods using arrays of
+/// \c FLEXTableViewSection objects provied by a special delegate.
+@interface FLEXFilteringTableViewController : FLEXTableViewController
+
+/// Stores the current search query.
+@property (nonatomic, copy) NSString *filterText;
+
+/// This property is set to \c self by default.
+///
+/// This property is used to power almost all of the table view's data source
+/// and delegate methods automatically, including row and section filtering
+/// when the user searches, 3D Touch context menus, row selection, etc.
+///
+/// Setting this property will also set \c searchDelegate to that object.
+@property (nonatomic, weak) id filterDelegate;
+
+/// Defaults to \c NO. If enabled, all filtering will be done by calling
+/// \c onBackgroundQueue:thenOnMainQueue: with the UI updated on the main queue.
+@property (nonatomic) BOOL filterInBackground;
+
+/// Defaults to \c NO. If enabled, one • will be supplied as an index title for each section.
+@property (nonatomic) BOOL wantsSectionIndexTitles;
+
+/// Recalculates the non-empty sections and reloads the table view.
+///
+/// Subclasses may override to perform additional reloading logic,
+/// such as calling \c -reloadSections if needed. Be sure to call
+/// \c super after any logic that would affect the appearance of
+/// the table view, since the table view is reloaded last.
+///
+/// Called at the end of this class's implementation of \c updateSearchResults:
+- (void)reloadData;
+
+/// Invoke this method to call \c -reloadData on each section
+/// in \c self.filterDelegate.allSections.
+- (void)reloadSections;
+
+#pragma mark FLEXTableViewFiltering
+
+@property (nonatomic, copy) NSArray *sections;
+@property (nonatomic, copy) NSArray *allSections;
+
+/// Subclasses can override to hide specific sections under certain conditions
+/// if using \c self as the \c filterDelegate, as is the default.
+///
+/// For example, the object explorer hides the description section when searching.
+@property (nonatomic, readonly) NSArray *nonemptySections;
+
+/// If using \c self as the \c filterDelegate, as is the default,
+/// subclasses should override to provide the sections for the table view.
+- (NSArray *)makeSections;
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXFilteringTableViewController.m b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXFilteringTableViewController.m
new file mode 100644
index 00000000..8b4e7306
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXFilteringTableViewController.m
@@ -0,0 +1,203 @@
+//
+// FLEXFilteringTableViewController.m
+// FLEX
+//
+// Created by Tanner on 3/9/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXFilteringTableViewController.h"
+#import "FLEXTableViewSection.h"
+#import "NSArray+FLEX.h"
+
+@interface FLEXFilteringTableViewController ()
+
+@end
+
+@implementation FLEXFilteringTableViewController
+@synthesize allSections = _allSections;
+
+#pragma mark - View controller lifecycle
+
+- (void)loadView {
+ [super loadView];
+
+ if (!self.filterDelegate) {
+ self.filterDelegate = self;
+ } else {
+ [self _registerCellsForReuse];
+ }
+}
+
+- (void)_registerCellsForReuse {
+ for (FLEXTableViewSection *section in self.filterDelegate.allSections) {
+ if (section.cellRegistrationMapping) {
+ [self.tableView registerCells:section.cellRegistrationMapping];
+ }
+ }
+}
+
+
+#pragma mark - Public
+
+- (void)setFilterDelegate:(id)filterDelegate {
+ _filterDelegate = filterDelegate;
+ filterDelegate.allSections = [filterDelegate makeSections];
+
+ if (self.isViewLoaded) {
+ [self _registerCellsForReuse];
+ }
+}
+
+- (void)reloadData {
+ [self reloadData:self.nonemptySections];
+}
+
+- (void)reloadData:(NSArray *)nonemptySections {
+ // Recalculate displayed sections
+ self.filterDelegate.sections = nonemptySections;
+
+ // Refresh table view
+ if (self.isViewLoaded) {
+ [self.tableView reloadData];
+ }
+}
+
+- (void)reloadSections {
+ for (FLEXTableViewSection *section in self.filterDelegate.allSections) {
+ [section reloadData];
+ }
+}
+
+
+#pragma mark - Search
+
+- (void)updateSearchResults:(NSString *)newText {
+ NSArray *(^filter)() = ^NSArray *{
+ self.filterText = newText;
+
+ // Sections will adjust data based on this property
+ for (FLEXTableViewSection *section in self.filterDelegate.allSections) {
+ section.filterText = newText;
+ }
+
+ return nil;
+ };
+
+ if (self.filterInBackground) {
+ [self onBackgroundQueue:filter thenOnMainQueue:^(NSArray *unused) {
+ if ([self.searchText isEqualToString:newText]) {
+ [self reloadData];
+ }
+ }];
+ } else {
+ filter();
+ [self reloadData];
+ }
+}
+
+
+#pragma mark Filtering
+
+- (NSArray *)nonemptySections {
+ return [self.filterDelegate.allSections flex_filtered:^BOOL(FLEXTableViewSection *section, NSUInteger idx) {
+ return section.numberOfRows > 0;
+ }];
+}
+
+- (NSArray *)makeSections {
+ return @[];
+}
+
+- (void)setAllSections:(NSArray *)allSections {
+ _allSections = allSections.copy;
+ self.sections = self.nonemptySections;
+}
+
+
+#pragma mark - UITableViewDataSource
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+ return self.filterDelegate.sections.count;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return self.filterDelegate.sections[section].numberOfRows;
+}
+
+- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
+ return self.filterDelegate.sections[section].title;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ NSString *reuse = [self.filterDelegate.sections[indexPath.section] reuseIdentifierForRow:indexPath.row];
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuse forIndexPath:indexPath];
+ [self.filterDelegate.sections[indexPath.section] configureCell:cell forRow:indexPath.row];
+ return cell;
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return UITableViewAutomaticDimension;
+}
+
+- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
+ if (self.wantsSectionIndexTitles) {
+ return [NSArray flex_forEachUpTo:self.filterDelegate.sections.count map:^id(NSUInteger i) {
+ return @"⦁";
+ }];
+ }
+
+ return nil;
+}
+
+
+#pragma mark - UITableViewDelegate
+
+- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
+ return [self.filterDelegate.sections[indexPath.section] canSelectRow:indexPath.row];
+}
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ FLEXTableViewSection *section = self.filterDelegate.sections[indexPath.section];
+
+ void (^action)(UIViewController *) = [section didSelectRowAction:indexPath.row];
+ UIViewController *details = [section viewControllerToPushForRow:indexPath.row];
+
+ if (action) {
+ action(self);
+ [tableView deselectRowAtIndexPath:indexPath animated:YES];
+ } else if (details) {
+ [self.navigationController pushViewController:details animated:YES];
+ } else {
+ [NSException raise:NSInternalInconsistencyException
+ format:@"Row is selectable but has no action or view controller"];
+ }
+}
+
+- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
+ [self.filterDelegate.sections[indexPath.section] didPressInfoButtonAction:indexPath.row](self);
+}
+
+#if FLEX_AT_LEAST_IOS13_SDK
+
+- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) {
+ FLEXTableViewSection *section = self.filterDelegate.sections[indexPath.section];
+ NSString *title = [section menuTitleForRow:indexPath.row];
+ NSArray *menuItems = [section menuItemsForRow:indexPath.row sender:self];
+
+ if (menuItems.count) {
+ return [UIContextMenuConfiguration
+ configurationWithIdentifier:nil
+ previewProvider:nil
+ actionProvider:^UIMenu *(NSArray *suggestedActions) {
+ return [UIMenu menuWithTitle:title children:menuItems];
+ }
+ ];
+ }
+
+ return nil;
+}
+
+#endif
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXNavigationController.h b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXNavigationController.h
new file mode 100644
index 00000000..6f8f1e10
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXNavigationController.h
@@ -0,0 +1,19 @@
+//
+// FLEXNavigationController.h
+// FLEX
+//
+// Created by Tanner on 1/30/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FLEXNavigationController : UINavigationController
+
++ (instancetype)withRootViewController:(UIViewController *)rootVC;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXNavigationController.m b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXNavigationController.m
new file mode 100644
index 00000000..27731763
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXNavigationController.m
@@ -0,0 +1,178 @@
+//
+// FLEXNavigationController.m
+// FLEX
+//
+// Created by Tanner on 1/30/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXNavigationController.h"
+#import "FLEXExplorerViewController.h"
+#import "FLEXTabList.h"
+
+@interface UINavigationController (Private)
+- (void)_gestureRecognizedInteractiveHide:(UIGestureRecognizer *)sender;
+@end
+@interface UIPanGestureRecognizer (Private)
+- (void)_setDelegate:(id)delegate;
+@end
+
+@interface FLEXNavigationController ()
+@property (nonatomic, readonly) BOOL toolbarWasHidden;
+@property (nonatomic) BOOL waitingToAddTab;
+@property (nonatomic) BOOL didSetupPendingDismissButtons;
+@property (nonatomic) UISwipeGestureRecognizer *navigationBarSwipeGesture;
+@end
+
+@implementation FLEXNavigationController
+
++ (instancetype)withRootViewController:(UIViewController *)rootVC {
+ return [[self alloc] initWithRootViewController:rootVC];
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.waitingToAddTab = YES;
+
+ // Add gesture to reveal toolbar if hidden
+ self.navigationBar.userInteractionEnabled = YES;
+ [self.navigationBar addGestureRecognizer:[[UITapGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleNavigationBarTap:)
+ ]];
+
+ // Add gesture to dismiss if not presented with a sheet style
+ if (@available(iOS 13, *)) {
+ switch (self.modalPresentationStyle) {
+ case UIModalPresentationAutomatic:
+ case UIModalPresentationPageSheet:
+ case UIModalPresentationFormSheet:
+ break;
+
+ default:
+ [self addNavigationBarSwipeGesture];
+ break;
+ }
+ } else {
+ [self addNavigationBarSwipeGesture];
+ }
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+
+ if (self.beingPresented && !self.didSetupPendingDismissButtons) {
+ for (UIViewController *vc in self.viewControllers) {
+ [self addNavigationBarItemsToViewController:vc.navigationItem];
+ }
+
+ self.didSetupPendingDismissButtons = YES;
+ }
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+
+ if (self.waitingToAddTab) {
+ // Only add new tab if we're presented properly
+ if ([self.presentingViewController isKindOfClass:[FLEXExplorerViewController class]]) {
+ // New navigation controllers always add themselves as new tabs,
+ // tabs are closed by FLEXExplorerViewController
+ [FLEXTabList.sharedList addTab:self];
+ self.waitingToAddTab = NO;
+ }
+ }
+}
+
+- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
+ [super pushViewController:viewController animated:animated];
+ [self addNavigationBarItemsToViewController:viewController.navigationItem];
+}
+
+- (void)dismissAnimated {
+ // Tabs are only closed if the done button is pressed; this
+ // allows you to leave a tab open by dragging down to dismiss
+ [FLEXTabList.sharedList closeTab:self];
+ [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
+}
+
+- (void)addNavigationBarItemsToViewController:(UINavigationItem *)navigationItem {
+ if (!self.presentingViewController) {
+ return;
+ }
+
+ // Check if a done item already exists
+ for (UIBarButtonItem *item in navigationItem.rightBarButtonItems) {
+ if (item.style == UIBarButtonItemStyleDone) {
+ return;
+ }
+ }
+
+ // Give root view controllers a Done button if it does not already have one
+ UIBarButtonItem *done = [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+ target:self
+ action:@selector(dismissAnimated)
+ ];
+
+ // Prepend the button if other buttons exist already
+ NSArray *existingItems = navigationItem.rightBarButtonItems;
+ if (existingItems.count) {
+ navigationItem.rightBarButtonItems = [@[done] arrayByAddingObjectsFromArray:existingItems];
+ } else {
+ navigationItem.rightBarButtonItem = done;
+ }
+
+ // Keeps us from calling this method again on
+ // the same view controllers in -viewWillAppear:
+ self.didSetupPendingDismissButtons = YES;
+}
+
+- (void)addNavigationBarSwipeGesture {
+ UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleNavigationBarSwipe:)
+ ];
+ swipe.direction = UISwipeGestureRecognizerDirectionDown;
+ swipe.delegate = self;
+ self.navigationBarSwipeGesture = swipe;
+ [self.navigationBar addGestureRecognizer:swipe];
+}
+
+- (void)handleNavigationBarSwipe:(UISwipeGestureRecognizer *)sender {
+ if (sender.state == UIGestureRecognizerStateRecognized) {
+ [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
+ }
+}
+
+- (void)handleNavigationBarTap:(UIGestureRecognizer *)sender {
+ if (sender.state == UIGestureRecognizerStateRecognized) {
+ if (self.toolbarHidden) {
+ [self setToolbarHidden:NO animated:YES];
+ }
+ }
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)g2 {
+ if (g1 == self.navigationBarSwipeGesture && g2 == self.barHideOnSwipeGestureRecognizer) {
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)_gestureRecognizedInteractiveHide:(UIPanGestureRecognizer *)sender {
+ if (sender.state == UIGestureRecognizerStateRecognized) {
+ BOOL show = self.topViewController.toolbarItems.count;
+ CGFloat yTranslation = [sender translationInView:self.view].y;
+ CGFloat yVelocity = [sender velocityInView:self.view].y;
+ if (yVelocity > 2000) {
+ [self setToolbarHidden:YES animated:YES];
+ } else if (show && yTranslation > 20 && yVelocity > 250) {
+ [self setToolbarHidden:NO animated:YES];
+ } else if (yTranslation < -20) {
+ [self setToolbarHidden:YES animated:YES];
+ }
+ }
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXTableViewController.h b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXTableViewController.h
new file mode 100644
index 00000000..230545a0
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXTableViewController.h
@@ -0,0 +1,149 @@
+//
+// FLEXTableViewController.h
+// FLEX
+//
+// Created by Tanner on 7/5/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import
+#import "FLEXTableView.h"
+@class FLEXScopeCarousel, FLEXWindow, FLEXTableViewSection;
+
+typedef CGFloat FLEXDebounceInterval;
+/// No delay, all events delivered
+extern CGFloat const kFLEXDebounceInstant;
+/// Small delay which makes UI seem smoother by avoiding rapid events
+extern CGFloat const kFLEXDebounceFast;
+/// Slower than Fast, faster than ExpensiveIO
+extern CGFloat const kFLEXDebounceForAsyncSearch;
+/// The least frequent, at just over once per second; for I/O or other expensive operations
+extern CGFloat const kFLEXDebounceForExpensiveIO;
+
+@protocol FLEXSearchResultsUpdating
+/// A method to handle search query update events.
+///
+/// \c searchBarDebounceInterval is used to reduce the frequency at which this
+/// method is called. This method is also called when the search bar becomes
+/// the first responder, and when the selected search bar scope index changes.
+- (void)updateSearchResults:(NSString *)newText;
+@end
+
+@interface FLEXTableViewController : UITableViewController <
+ UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate
+>
+
+/// A grouped table view. Inset on iOS 13.
+///
+/// Simply calls into \c initWithStyle:
+- (id)init;
+
+/// Subclasses may override to configure the controller before \c viewDidLoad:
+- (id)initWithStyle:(UITableViewStyle)style;
+
+@property (nonatomic) FLEXTableView *tableView;
+
+/// If your subclass conforms to \c FLEXSearchResultsUpdating
+/// then this property is assigned to \c self automatically.
+///
+/// Setting \c filterDelegate will also set this property to that object.
+@property (nonatomic, weak) id searchDelegate;
+
+/// Defaults to NO.
+///
+/// Setting this to YES will initialize the carousel and the view.
+@property (nonatomic) BOOL showsCarousel;
+/// A horizontally scrolling list with functionality similar to
+/// that of a search bar's scope bar. You'd want to use this when
+/// you have potentially more than 4 scope options.
+@property (nonatomic) FLEXScopeCarousel *carousel;
+
+/// Defaults to NO.
+///
+/// Setting this to YES will initialize searchController and the view.
+@property (nonatomic) BOOL showsSearchBar;
+/// Defaults to NO.
+///
+/// Setting this to YES will make the search bar appear whenever the view appears.
+/// Otherwise, iOS will only show the search bar when you scroll up.
+@property (nonatomic) BOOL showSearchBarInitially;
+
+/// nil unless showsSearchBar is set to YES.
+///
+/// self is used as the default search results updater and delegate.
+/// The search bar will not dim the background or hide the navigation bar by default.
+/// On iOS 11 and up, the search bar will appear in the navigation bar below the title.
+@property (nonatomic) UISearchController *searchController;
+/// Used to initialize the search controller. Defaults to nil.
+@property (nonatomic) UIViewController *searchResultsController;
+/// Defaults to "Fast"
+///
+/// Determines how often search bar results will be "debounced."
+/// Empty query events are always sent instantly. Query events will
+/// be sent when the user has not changed the query for this interval.
+@property (nonatomic) FLEXDebounceInterval searchBarDebounceInterval;
+/// Whether the search bar stays at the top of the view while scrolling.
+///
+/// Calls into self.navigationItem.hidesSearchBarWhenScrolling.
+/// Do not change self.navigationItem.hidesSearchBarWhenScrolling directly,
+/// or it will not be respsected. Use this instead.
+/// Defaults to NO.
+@property (nonatomic) BOOL pinSearchBar;
+/// By default, we will show the search bar's cancel button when
+/// search becomes active and hide it when search is dismissed.
+///
+/// Do not set the showsCancelButton property on the searchController's
+/// searchBar manually. Set this property after turning on showsSearchBar.
+///
+/// Does nothing pre-iOS 13, safe to call on any version.
+@property (nonatomic) BOOL automaticallyShowsSearchBarCancelButton;
+
+/// If using the scope bar, self.searchController.searchBar.selectedScopeButtonIndex.
+/// Otherwise, this is the selected index of the carousel, or NSNotFound if using neither.
+@property (nonatomic) NSInteger selectedScope;
+/// self.searchController.searchBar.text
+@property (nonatomic, readonly) NSString *searchText;
+
+/// A totally optional delegate to forward search results updater calls to.
+/// If a delegate is set, updateSearchResults: is not called on this view controller.
+@property (nonatomic, weak ) id searchResultsUpdater;
+
+/// self.view.window as a \c FLEXWindow
+@property (nonatomic, readonly) FLEXWindow *window;
+
+/// Convenient for doing some async processor-intensive searching
+/// in the background before updating the UI back on the main queue.
+- (void)onBackgroundQueue:(NSArray *(^)(void))backgroundBlock thenOnMainQueue:(void(^)(NSArray *))mainBlock;
+
+/// Adds up to 3 additional items to the toolbar in right-to-left order.
+///
+/// That is, the first item in the given array will be the rightmost item behind
+/// any existing toolbar items. By default, buttons for bookmarks and tabs are shown.
+///
+/// If you wish to have more control over how the buttons are arranged or which
+/// buttons are displayed, you can access the properties for the pre-existing
+/// toolbar items directly and manually set \c self.toolbarItems by overriding
+/// the \c setupToolbarItems method below.
+- (void)addToolbarItems:(NSArray *)items;
+
+/// Subclasses may override. You should not need to call this method directly.
+- (void)setupToolbarItems;
+
+@property (nonatomic, readonly) UIBarButtonItem *shareToolbarItem;
+@property (nonatomic, readonly) UIBarButtonItem *bookmarksToolbarItem;
+@property (nonatomic, readonly) UIBarButtonItem *openTabsToolbarItem;
+
+/// Whether or not to display the "share" icon in the middle of the toolbar. NO by default.
+///
+/// Turning this on after you have added custom toolbar items will
+/// push off the leftmost toolbar item and shift the others leftward.
+@property (nonatomic) BOOL showsShareToolbarItem;
+/// Called when the share button is pressed.
+/// Default implementation does nothign. Subclasses may override.
+- (void)shareButtonPressed:(UIBarButtonItem *)sender;
+
+/// Subclasses may call this to opt-out of all toolbar related behavior.
+/// This is necessary if you want to disable the gesture which reveals the toolbar.
+- (void)disableToolbar;
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXTableViewController.m b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXTableViewController.m
new file mode 100644
index 00000000..e493fa21
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Controllers/FLEXTableViewController.m
@@ -0,0 +1,590 @@
+//
+// FLEXTableViewController.m
+// FLEX
+//
+// Created by Tanner on 7/5/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableViewController.h"
+#import "FLEXExplorerViewController.h"
+#import "FLEXBookmarksViewController.h"
+#import "FLEXTabsViewController.h"
+#import "FLEXScopeCarousel.h"
+#import "FLEXTableView.h"
+#import "FLEXUtility.h"
+#import "FLEXResources.h"
+#import "UIBarButtonItem+FLEX.h"
+#import
+
+@interface Block : NSObject
+- (void)invoke;
+@end
+
+CGFloat const kFLEXDebounceInstant = 0.f;
+CGFloat const kFLEXDebounceFast = 0.05;
+CGFloat const kFLEXDebounceForAsyncSearch = 0.15;
+CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
+
+@interface FLEXTableViewController ()
+@property (nonatomic) NSTimer *debounceTimer;
+@property (nonatomic) BOOL didInitiallyRevealSearchBar;
+@property (nonatomic) UITableViewStyle style;
+
+@property (nonatomic) BOOL hasAppeared;
+@property (nonatomic, readonly) UIView *tableHeaderViewContainer;
+
+@property (nonatomic, readonly) BOOL manuallyDeactivateSearchOnDisappear;
+
+@property (nonatomic) UIBarButtonItem *middleToolbarItem;
+@property (nonatomic) UIBarButtonItem *middleLeftToolbarItem;
+@property (nonatomic) UIBarButtonItem *leftmostToolbarItem;
+@end
+
+@implementation FLEXTableViewController
+@dynamic tableView;
+@synthesize showsShareToolbarItem = _showsShareToolbarItem;
+@synthesize tableHeaderViewContainer = _tableHeaderViewContainer;
+@synthesize automaticallyShowsSearchBarCancelButton = _automaticallyShowsSearchBarCancelButton;
+
+#pragma mark - Initialization
+
+- (id)init {
+#if FLEX_AT_LEAST_IOS13_SDK
+ if (@available(iOS 13.0, *)) {
+ self = [self initWithStyle:UITableViewStyleInsetGrouped];
+ } else {
+ self = [self initWithStyle:UITableViewStyleGrouped];
+ }
+#else
+ self = [self initWithStyle:UITableViewStyleGrouped];
+#endif
+ return self;
+}
+
+- (id)initWithStyle:(UITableViewStyle)style {
+ self = [super initWithStyle:style];
+
+ if (self) {
+ _searchBarDebounceInterval = kFLEXDebounceFast;
+ _showSearchBarInitially = YES;
+ _style = style;
+ _manuallyDeactivateSearchOnDisappear = ({
+ NSProcessInfo.processInfo.operatingSystemVersion.majorVersion < 11;
+ });
+
+ // We will be our own search delegate if we implement this method
+ if ([self respondsToSelector:@selector(updateSearchResults:)]) {
+ self.searchDelegate = (id)self;
+ }
+ }
+
+ return self;
+}
+
+
+#pragma mark - Public
+
+- (FLEXWindow *)window {
+ return (id)self.view.window;
+}
+
+- (void)setShowsSearchBar:(BOOL)showsSearchBar {
+ if (_showsSearchBar == showsSearchBar) return;
+ _showsSearchBar = showsSearchBar;
+
+ if (showsSearchBar) {
+ UIViewController *results = self.searchResultsController;
+ self.searchController = [[UISearchController alloc] initWithSearchResultsController:results];
+ self.searchController.searchBar.placeholder = @"Filter";
+ self.searchController.searchResultsUpdater = (id)self;
+ self.searchController.delegate = (id)self;
+ self.searchController.dimsBackgroundDuringPresentation = NO;
+ self.searchController.hidesNavigationBarDuringPresentation = NO;
+ /// Not necessary in iOS 13; remove this when iOS 13 is the minimum deployment target
+ self.searchController.searchBar.delegate = self;
+
+ self.automaticallyShowsSearchBarCancelButton = YES;
+
+ #if FLEX_AT_LEAST_IOS13_SDK
+ if (@available(iOS 13, *)) {
+ self.searchController.automaticallyShowsScopeBar = NO;
+ }
+ #endif
+
+ [self addSearchController:self.searchController];
+ } else {
+ // Search already shown and just set to NO, so remove it
+ [self removeSearchController:self.searchController];
+ }
+}
+
+- (void)setShowsCarousel:(BOOL)showsCarousel {
+ if (_showsCarousel == showsCarousel) return;
+ _showsCarousel = showsCarousel;
+
+ if (showsCarousel) {
+ _carousel = ({
+ __weak __typeof(self) weakSelf = self;
+
+ FLEXScopeCarousel *carousel = [FLEXScopeCarousel new];
+ carousel.selectedIndexChangedAction = ^(NSInteger idx) {
+ __typeof(self) self = weakSelf;
+ [self.searchDelegate updateSearchResults:self.searchText];
+ };
+
+ // UITableView won't update the header size unless you reset the header view
+ [carousel registerBlockForDynamicTypeChanges:^(FLEXScopeCarousel *carousel) {
+ __typeof(self) self = weakSelf;
+ [self layoutTableHeaderIfNeeded];
+ }];
+
+ carousel;
+ });
+ [self addCarousel:_carousel];
+ } else {
+ // Carousel already shown and just set to NO, so remove it
+ [self removeCarousel:_carousel];
+ }
+}
+
+- (NSInteger)selectedScope {
+ if (self.searchController.searchBar.showsScopeBar) {
+ return self.searchController.searchBar.selectedScopeButtonIndex;
+ } else if (self.showsCarousel) {
+ return self.carousel.selectedIndex;
+ } else {
+ return 0;
+ }
+}
+
+- (void)setSelectedScope:(NSInteger)selectedScope {
+ if (self.searchController.searchBar.showsScopeBar) {
+ self.searchController.searchBar.selectedScopeButtonIndex = selectedScope;
+ } else if (self.showsCarousel) {
+ self.carousel.selectedIndex = selectedScope;
+ }
+
+ [self.searchDelegate updateSearchResults:self.searchText];
+}
+
+- (NSString *)searchText {
+ return self.searchController.searchBar.text;
+}
+
+- (BOOL)automaticallyShowsSearchBarCancelButton {
+#if FLEX_AT_LEAST_IOS13_SDK
+ if (@available(iOS 13, *)) {
+ return self.searchController.automaticallyShowsCancelButton;
+ }
+#endif
+
+ return _automaticallyShowsSearchBarCancelButton;
+}
+
+- (void)setAutomaticallyShowsSearchBarCancelButton:(BOOL)value {
+#if FLEX_AT_LEAST_IOS13_SDK
+ if (@available(iOS 13, *)) {
+ self.searchController.automaticallyShowsCancelButton = value;
+ }
+#endif
+
+ _automaticallyShowsSearchBarCancelButton = value;
+}
+
+- (void)onBackgroundQueue:(NSArray *(^)(void))backgroundBlock thenOnMainQueue:(void(^)(NSArray *))mainBlock {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NSArray *items = backgroundBlock();
+ dispatch_async(dispatch_get_main_queue(), ^{
+ mainBlock(items);
+ });
+ });
+}
+
+- (void)setsShowsShareToolbarItem:(BOOL)showsShareToolbarItem {
+ _showsShareToolbarItem = showsShareToolbarItem;
+ if (self.isViewLoaded) {
+ [self setupToolbarItems];
+ }
+}
+
+- (void)disableToolbar {
+ self.navigationController.toolbarHidden = YES;
+ self.navigationController.hidesBarsOnSwipe = NO;
+ self.toolbarItems = nil;
+}
+
+
+#pragma mark - View Controller Lifecycle
+
+- (void)loadView {
+ self.view = [FLEXTableView style:self.style];
+ self.tableView.dataSource = self;
+ self.tableView.delegate = self;
+
+ _shareToolbarItem = FLEXBarButtonItemSystem(Action, self, @selector(shareButtonPressed:));
+ _bookmarksToolbarItem = [UIBarButtonItem
+ itemWithImage:FLEXResources.bookmarksIcon target:self action:@selector(showBookmarks)
+ ];
+ _openTabsToolbarItem = [UIBarButtonItem
+ itemWithImage:FLEXResources.openTabsIcon target:self action:@selector(showTabSwitcher)
+ ];
+
+ self.leftmostToolbarItem = UIBarButtonItem.flex_fixedSpace;
+ self.middleLeftToolbarItem = UIBarButtonItem.flex_fixedSpace;
+ self.middleToolbarItem = UIBarButtonItem.flex_fixedSpace;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
+
+ // Toolbar
+ self.navigationController.toolbarHidden = NO;
+ self.navigationController.hidesBarsOnSwipe = YES;
+
+ // On iOS 13, the root view controller shows it's search bar no matter what.
+ // Turning this off avoids some weird flash the navigation bar does when we
+ // toggle navigationItem.hidesSearchBarWhenScrolling on and off. The flash
+ // will still happen on subsequent view controllers, but we can at least
+ // avoid it for the root view controller
+ if (@available(iOS 13, *)) {
+ if (self.navigationController.viewControllers.firstObject == self) {
+ _showSearchBarInitially = NO;
+ }
+ }
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+
+ // When going back, make the search bar reappear instead of hiding
+ if (@available(iOS 11.0, *)) {
+ if ((self.pinSearchBar || self.showSearchBarInitially) && !self.didInitiallyRevealSearchBar) {
+ self.navigationItem.hidesSearchBarWhenScrolling = NO;
+ }
+ }
+
+ [self setupToolbarItems];
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+
+ // Allow scrolling to collapse the search bar, only if we don't want it pinned
+ if (@available(iOS 11.0, *)) {
+ if (self.showSearchBarInitially && !self.pinSearchBar && !self.didInitiallyRevealSearchBar) {
+ // All this mumbo jumbo is necessary to work around a bug in iOS 13 up to 13.2
+ // wherein quickly toggling navigationItem.hidesSearchBarWhenScrolling to make
+ // the search bar appear initially results in a bugged search bar that
+ // becomes transparent and floats over the screen as you scroll
+ [UIView animateWithDuration:0.2 animations:^{
+ self.navigationItem.hidesSearchBarWhenScrolling = YES;
+ [self.navigationController.view setNeedsLayout];
+ [self.navigationController.view layoutIfNeeded];
+ }];
+ }
+ }
+
+ // We only want to reveal the search bar when the view controller first appears.
+ self.didInitiallyRevealSearchBar = YES;
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+
+ if (self.manuallyDeactivateSearchOnDisappear && self.searchController.isActive) {
+ self.searchController.active = NO;
+ }
+}
+
+- (void)didMoveToParentViewController:(UIViewController *)parent {
+ [super didMoveToParentViewController:parent];
+ // Reset this since we are re-appearing under a new
+ // parent view controller and need to show it again
+ self.didInitiallyRevealSearchBar = NO;
+}
+
+
+#pragma mark - Toolbar, Public
+
+- (void)setupToolbarItems {
+ if (!self.isViewLoaded) {
+ return;
+ }
+
+ self.toolbarItems = @[
+ self.leftmostToolbarItem,
+ UIBarButtonItem.flex_flexibleSpace,
+ self.middleLeftToolbarItem,
+ UIBarButtonItem.flex_flexibleSpace,
+ self.middleToolbarItem,
+ UIBarButtonItem.flex_flexibleSpace,
+ self.bookmarksToolbarItem,
+ UIBarButtonItem.flex_flexibleSpace,
+ self.openTabsToolbarItem,
+ ];
+
+ for (UIBarButtonItem *item in self.toolbarItems) {
+ [item _setWidth:60];
+ // This does not work for anything but fixed spaces for some reason
+ // item.width = 60;
+ }
+
+ // Disable tabs entirely when not presented by FLEXExplorerViewController
+ UIViewController *presenter = self.navigationController.presentingViewController;
+ if (![presenter isKindOfClass:[FLEXExplorerViewController class]]) {
+ self.openTabsToolbarItem.enabled = NO;
+ }
+}
+
+- (void)addToolbarItems:(NSArray *)items {
+ if (self.showsShareToolbarItem) {
+ // Share button is in the middle, skip middle button
+ if (items.count > 0) {
+ self.middleLeftToolbarItem = items[0];
+ }
+ if (items.count > 1) {
+ self.leftmostToolbarItem = items[1];
+ }
+ } else {
+ // Add buttons right-to-left
+ if (items.count > 0) {
+ self.middleToolbarItem = items[0];
+ }
+ if (items.count > 1) {
+ self.middleLeftToolbarItem = items[1];
+ }
+ if (items.count > 2) {
+ self.leftmostToolbarItem = items[2];
+ }
+ }
+
+ [self setupToolbarItems];
+}
+
+- (void)setShowsShareToolbarItem:(BOOL)showShare {
+ if (_showsShareToolbarItem != showShare) {
+ _showsShareToolbarItem = showShare;
+
+ if (showShare) {
+ // Push out leftmost item
+ self.leftmostToolbarItem = self.middleLeftToolbarItem;
+ self.middleLeftToolbarItem = self.middleToolbarItem;
+
+ // Use share for middle
+ self.middleToolbarItem = self.shareToolbarItem;
+ } else {
+ // Remove share, shift custom items rightward
+ self.middleToolbarItem = self.middleLeftToolbarItem;
+ self.middleLeftToolbarItem = self.leftmostToolbarItem;
+ self.leftmostToolbarItem = UIBarButtonItem.flex_fixedSpace;
+ }
+ }
+
+ [self setupToolbarItems];
+}
+
+- (void)shareButtonPressed:(UIBarButtonItem *)sender {
+
+}
+
+
+#pragma mark - Private
+
+- (void)debounce:(void(^)(void))block {
+ [self.debounceTimer invalidate];
+
+ self.debounceTimer = [NSTimer
+ scheduledTimerWithTimeInterval:self.searchBarDebounceInterval
+ target:block
+ selector:@selector(invoke)
+ userInfo:nil
+ repeats:NO
+ ];
+}
+
+- (void)layoutTableHeaderIfNeeded {
+ if (self.showsCarousel) {
+ self.carousel.frame = FLEXRectSetHeight(
+ self.carousel.frame, self.carousel.intrinsicContentSize.height
+ );
+ }
+
+ self.tableView.tableHeaderView = self.tableView.tableHeaderView;
+}
+
+- (void)addCarousel:(FLEXScopeCarousel *)carousel {
+ if (@available(iOS 11.0, *)) {
+ self.tableView.tableHeaderView = carousel;
+ } else {
+ carousel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
+
+ CGRect frame = self.tableHeaderViewContainer.frame;
+ CGRect subviewFrame = carousel.frame;
+ subviewFrame.origin.y = 0;
+
+ // Put the carousel below the search bar if it's already there
+ if (self.showsSearchBar) {
+ carousel.frame = subviewFrame = FLEXRectSetY(
+ subviewFrame, self.searchController.searchBar.frame.size.height
+ );
+ frame.size.height += carousel.intrinsicContentSize.height;
+ } else {
+ frame.size.height = carousel.intrinsicContentSize.height;
+ }
+
+ self.tableHeaderViewContainer.frame = frame;
+ [self.tableHeaderViewContainer addSubview:carousel];
+ }
+
+ [self layoutTableHeaderIfNeeded];
+}
+
+- (void)removeCarousel:(FLEXScopeCarousel *)carousel {
+ [carousel removeFromSuperview];
+
+ if (@available(iOS 11.0, *)) {
+ self.tableView.tableHeaderView = nil;
+ } else {
+ if (self.showsSearchBar) {
+ [self removeSearchController:self.searchController];
+ [self addSearchController:self.searchController];
+ } else {
+ self.tableView.tableHeaderView = nil;
+ _tableHeaderViewContainer = nil;
+ }
+ }
+}
+
+- (void)addSearchController:(UISearchController *)controller {
+ if (@available(iOS 11.0, *)) {
+ self.navigationItem.searchController = controller;
+ } else {
+ controller.searchBar.autoresizingMask |= UIViewAutoresizingFlexibleBottomMargin;
+ [self.tableHeaderViewContainer addSubview:controller.searchBar];
+ CGRect subviewFrame = controller.searchBar.frame;
+ CGRect frame = self.tableHeaderViewContainer.frame;
+ frame.size.width = MAX(frame.size.width, subviewFrame.size.width);
+ frame.size.height = subviewFrame.size.height;
+
+ // Move the carousel down if it's already there
+ if (self.showsCarousel) {
+ self.carousel.frame = FLEXRectSetY(
+ self.carousel.frame, subviewFrame.size.height
+ );
+ frame.size.height += self.carousel.frame.size.height;
+ }
+
+ self.tableHeaderViewContainer.frame = frame;
+ [self layoutTableHeaderIfNeeded];
+ }
+}
+
+- (void)removeSearchController:(UISearchController *)controller {
+ if (@available(iOS 11.0, *)) {
+ self.navigationItem.searchController = nil;
+ } else {
+ [controller.searchBar removeFromSuperview];
+
+ if (self.showsCarousel) {
+// self.carousel.frame = FLEXRectRemake(CGPointZero, self.carousel.frame.size);
+ [self removeCarousel:self.carousel];
+ [self addCarousel:self.carousel];
+ } else {
+ self.tableView.tableHeaderView = nil;
+ _tableHeaderViewContainer = nil;
+ }
+ }
+}
+
+- (UIView *)tableHeaderViewContainer {
+ if (!_tableHeaderViewContainer) {
+ _tableHeaderViewContainer = [UIView new];
+ self.tableView.tableHeaderView = self.tableHeaderViewContainer;
+ }
+
+ return _tableHeaderViewContainer;
+}
+
+- (void)showBookmarks {
+ UINavigationController *nav = [[UINavigationController alloc]
+ initWithRootViewController:[FLEXBookmarksViewController new]
+ ];
+ [self presentViewController:nav animated:YES completion:nil];
+}
+
+- (void)showTabSwitcher {
+ UINavigationController *nav = [[UINavigationController alloc]
+ initWithRootViewController:[FLEXTabsViewController new]
+ ];
+ [self presentViewController:nav animated:YES completion:nil];
+}
+
+
+#pragma mark - Search Bar
+
+#pragma mark UISearchResultsUpdating
+
+- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
+ [self.debounceTimer invalidate];
+ NSString *text = searchController.searchBar.text;
+
+ void (^updateSearchResults)() = ^{
+ if (self.searchResultsUpdater) {
+ [self.searchResultsUpdater updateSearchResults:text];
+ } else {
+ [self.searchDelegate updateSearchResults:text];
+ }
+ };
+
+ // Only debounce if we want to, and if we have a non-empty string
+ // Empty string events are sent instantly
+ if (text.length && self.searchBarDebounceInterval > kFLEXDebounceInstant) {
+ [self debounce:updateSearchResults];
+ } else {
+ updateSearchResults();
+ }
+}
+
+
+#pragma mark UISearchControllerDelegate
+
+- (void)willPresentSearchController:(UISearchController *)searchController {
+ // Manually show cancel button for < iOS 13
+ if (!@available(iOS 13, *) && self.automaticallyShowsSearchBarCancelButton) {
+ [searchController.searchBar setShowsCancelButton:YES animated:YES];
+ }
+}
+
+- (void)willDismissSearchController:(UISearchController *)searchController {
+ // Manually hide cancel button for < iOS 13
+ if (!@available(iOS 13, *) && self.automaticallyShowsSearchBarCancelButton) {
+ [searchController.searchBar setShowsCancelButton:NO animated:YES];
+ }
+}
+
+
+#pragma mark UISearchBarDelegate
+
+/// Not necessary in iOS 13; remove this when iOS 13 is the deployment target
+- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope {
+ [self updateSearchResultsForSearchController:self.searchController];
+}
+
+
+#pragma mark Table View
+
+/// Not having a title in the first section looks weird with a rounded-corner table view style
+- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
+ if (@available(iOS 13, *)) {
+ if (self.style == UITableViewStyleInsetGrouped) {
+ return @" ";
+ }
+ }
+
+ return nil; // For plain/gropued style
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXSingleRowSection.h b/xcode/Pods/FLEX/Classes/Core/FLEXSingleRowSection.h
new file mode 100644
index 00000000..3ba81ee7
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/FLEXSingleRowSection.h
@@ -0,0 +1,28 @@
+//
+// FLEXSingleRowSection.h
+// FLEX
+//
+// Created by Tanner Bennett on 9/25/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableViewSection.h"
+
+/// A section providing a specific single row.
+///
+/// You may optionally provide a view controller to push when the row
+/// is selected, or an action to perform when it is selected.
+/// Which one is used first is up to the table view data source.
+@interface FLEXSingleRowSection : FLEXTableViewSection
+
+/// @param reuseIdentifier if nil, kFLEXDefaultCell is used.
++ (instancetype)title:(NSString *)sectionTitle
+ reuse:(NSString *)reuseIdentifier
+ cell:(void(^)(__kindof UITableViewCell *cell))cellConfiguration;
+
+@property (nonatomic) UIViewController *pushOnSelection;
+@property (nonatomic) void (^selectionAction)(UIViewController *host);
+/// Called to determine whether the single row should display itself or not.
+@property (nonatomic) BOOL (^filterMatcher)(NSString *filterText);
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXSingleRowSection.m b/xcode/Pods/FLEX/Classes/Core/FLEXSingleRowSection.m
new file mode 100644
index 00000000..95f557e8
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/FLEXSingleRowSection.m
@@ -0,0 +1,87 @@
+//
+// FLEXSingleRowSection.m
+// FLEX
+//
+// Created by Tanner Bennett on 9/25/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import "FLEXSingleRowSection.h"
+#import "FLEXTableView.h"
+
+@interface FLEXSingleRowSection ()
+@property (nonatomic, readonly) NSString *reuseIdentifier;
+@property (nonatomic, readonly) void (^cellConfiguration)(__kindof UITableViewCell *cell);
+
+@property (nonatomic) NSString *lastTitle;
+@property (nonatomic) NSString *lastSubitle;
+@end
+
+@implementation FLEXSingleRowSection
+
+#pragma mark - Public
+
++ (instancetype)title:(NSString *)title
+ reuse:(NSString *)reuse
+ cell:(void (^)(__kindof UITableViewCell *))config {
+ return [[self alloc] initWithTitle:title reuse:reuse cell:config];
+}
+
+- (id)initWithTitle:(NSString *)sectionTitle
+ reuse:(NSString *)reuseIdentifier
+ cell:(void (^)(__kindof UITableViewCell *))cellConfiguration {
+ self = [super init];
+ if (self) {
+ _title = sectionTitle;
+ _reuseIdentifier = reuseIdentifier ?: kFLEXDefaultCell;
+ _cellConfiguration = cellConfiguration;
+ }
+
+ return self;
+}
+
+#pragma mark - Overrides
+
+- (NSInteger)numberOfRows {
+ if (self.filterMatcher && self.filterText.length) {
+ return self.filterMatcher(self.filterText) ? 1 : 0;
+ }
+
+ return 1;
+}
+
+- (BOOL)canSelectRow:(NSInteger)row {
+ return self.pushOnSelection || self.selectionAction;
+}
+
+- (void (^)(__kindof UIViewController *))didSelectRowAction:(NSInteger)row {
+ return self.selectionAction;
+}
+
+- (UIViewController *)viewControllerToPushForRow:(NSInteger)row {
+ return self.pushOnSelection;
+}
+
+- (NSString *)reuseIdentifierForRow:(NSInteger)row {
+ return self.reuseIdentifier;
+}
+
+- (void)configureCell:(__kindof UITableViewCell *)cell forRow:(NSInteger)row {
+ cell.textLabel.text = nil;
+ cell.detailTextLabel.text = nil;
+ cell.accessoryType = UITableViewCellAccessoryNone;
+
+ self.cellConfiguration(cell);
+ self.lastTitle = cell.textLabel.text;
+ self.lastSubitle = cell.detailTextLabel.text;
+}
+
+- (NSString *)titleForRow:(NSInteger)row {
+ return self.lastTitle;
+}
+
+- (NSString *)subtitleForRow:(NSInteger)row {
+ return self.lastSubitle;
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXTableViewController.h b/xcode/Pods/FLEX/Classes/Core/FLEXTableViewController.h
deleted file mode 100644
index 561f68ae..00000000
--- a/xcode/Pods/FLEX/Classes/Core/FLEXTableViewController.h
+++ /dev/null
@@ -1,94 +0,0 @@
-//
-// FLEXTableViewController.h
-// FLEX
-//
-// Created by Tanner on 7/5/19.
-// Copyright © 2019 Flipboard. All rights reserved.
-//
-
-#import
-@class FLEXScopeCarousel;
-
-typedef CGFloat FLEXDebounceInterval;
-/// No delay, all events delivered
-extern CGFloat const kFLEXDebounceInstant;
-/// Small delay which makes UI seem smoother by avoiding rapid events
-extern CGFloat const kFLEXDebounceFast;
-/// Slower than Fast, faster than ExpensiveIO
-extern CGFloat const kFLEXDebounceForAsyncSearch;
-/// The least frequent, at just over once per second; for I/O or other expensive operations
-extern CGFloat const kFLEXDebounceForExpensiveIO;
-
-@interface FLEXTableViewController : UITableViewController
-
-/// A grouped table view. Inset on iOS 13.
-///
-/// Simply calls into initWithStyle:
-- (id)init;
-
-/// Defaults to NO.
-///
-/// Setting this to YES will initialize the carousel and the view.
-@property (nonatomic) BOOL showsCarousel;
-/// A horizontally scrolling list with functionality similar to
-/// that of a search bar's scope bar. You'd want to use this when
-/// you have potentially more than 4 scope options.
-@property (nonatomic) FLEXScopeCarousel *carousel;
-
-/// Defaults to NO.
-///
-/// Setting this to YES will initialize searchController and the view.
-@property (nonatomic) BOOL showsSearchBar;
-/// Defaults to NO.
-///
-/// Setting this to YES will make the search bar appear whenever the view appears.
-/// Otherwise, iOS will only show the search bar when you scroll up.
-@property (nonatomic) BOOL hideSearchBarInitially;
-
-/// nil unless showsSearchBar is set to YES.
-///
-/// self is used as the default search results updater and delegate.
-/// Make sure your subclass conforms to UISearchControllerDelegate.
-/// The search bar will not dim the background or hide the navigation bar by default.
-/// On iOS 11 and up, the search bar will appear in the navigation bar below the title.
-@property (nonatomic) UISearchController *searchController;
-/// Used to initialize the search controller. Defaults to nil.
-@property (nonatomic) UIViewController *searchResultsController;
-/// Defaults to "Fast"
-///
-/// Determines how often search bar results will be "debounced."
-/// Empty query events are always sent instantly. Query events will
-/// be sent when the user has not changed the query for this interval.
-@property (nonatomic) FLEXDebounceInterval searchBarDebounceInterval;
-/// Whether the search bar stays at the top of the view while scrolling.
-///
-/// Calls into self.navigationItem.hidesSearchBarWhenScrolling.
-/// Do not change self.navigationItem.hidesSearchBarWhenScrolling directly,
-/// or it will not be respsected. Use this instead.
-/// Defaults to NO.
-@property (nonatomic) BOOL pinSearchBar;
-/// By default, we will show the search bar's cancel button when
-/// search becomes active and hide it when search is dismissed.
-///
-/// Do not set the showsCancelButton property on the searchController's
-/// searchBar manually.
-@property (nonatomic) BOOL automaticallyShowsSearchBarCancelButton;
-
-/// If using the scope bar, self.searchController.searchBar.selectedScopeButtonIndex.
-/// Otherwise, this is the selected index of the carousel, or NSNotFound if using neither.
-@property (nonatomic, readonly) NSInteger selectedScope;
-/// self.searchController.searchBar.text
-@property (nonatomic, readonly) NSString *searchText;
-
-/// Subclasses should override to handle search query update events.
-///
-/// searchBarDebounceInterval is used to reduce the frequency at which this method is called.
-/// This method is also called when the search bar becomes the first responder,
-/// and when the selected search bar scope index changes.
-- (void)updateSearchResults:(NSString *)newText;
-
-/// Convenient for doing some async processor-intensive searching
-/// in the background before updating the UI back on the main queue.
-- (void)onBackgroundQueue:(NSArray *(^)())backgroundBlock thenOnMainQueue:(void(^)(NSArray *))mainBlock;
-
-@end
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXTableViewController.m b/xcode/Pods/FLEX/Classes/Core/FLEXTableViewController.m
deleted file mode 100644
index 9a50b0f0..00000000
--- a/xcode/Pods/FLEX/Classes/Core/FLEXTableViewController.m
+++ /dev/null
@@ -1,229 +0,0 @@
-//
-// FLEXTableViewController.m
-// FLEX
-//
-// Created by Tanner on 7/5/19.
-// Copyright © 2019 Flipboard. All rights reserved.
-//
-
-#import "FLEXTableViewController.h"
-#import "FLEXScopeCarousel.h"
-#import "FLEXTableView.h"
-#import
-
-@interface Block : NSObject
-- (void)invoke;
-@end
-
-CGFloat const kFLEXDebounceInstant = 0.f;
-CGFloat const kFLEXDebounceFast = 0.05;
-CGFloat const kFLEXDebounceForAsyncSearch = 0.15;
-CGFloat const kFLEXDebounceForExpensiveIO = 0.5;
-
-@interface FLEXTableViewController ()
-@property (nonatomic) NSTimer *debounceTimer;
-
-@end
-
-@implementation FLEXTableViewController
-
-#pragma mark - Public
-
-- (id)init {
-#if FLEX_AT_LEAST_IOS13_SDK
- if (@available(iOS 13.0, *)) {
- self = [self initWithStyle:UITableViewStyleInsetGrouped];
- } else {
- self = [self initWithStyle:UITableViewStyleGrouped];
- }
-#else
- self = [self initWithStyle:UITableViewStyleGrouped];
-#endif
- return self;
-}
-
-- (id)initWithStyle:(UITableViewStyle)style {
- self = [super initWithStyle:style];
-
- if (self) {
- self.searchBarDebounceInterval = kFLEXDebounceFast;
- }
-
- return self;
-}
-
-- (void)setShowsSearchBar:(BOOL)showsSearchBar {
- if (_showsSearchBar == showsSearchBar) return;
- _showsSearchBar = showsSearchBar;
-
- UIViewController *results = self.searchResultsController;
- self.searchController = [[UISearchController alloc] initWithSearchResultsController:results];
- self.searchController.searchBar.placeholder = @"Filter";
- self.searchController.searchResultsUpdater = (id)self;
- self.searchController.delegate = (id)self;
- self.searchController.dimsBackgroundDuringPresentation = NO;
- self.searchController.hidesNavigationBarDuringPresentation = NO;
- /// Not necessary in iOS 13; remove this when iOS 13 is the deployment target
- self.searchController.searchBar.delegate = self;
-
- if (@available(iOS 11.0, *)) {
- self.navigationItem.searchController = self.searchController;
- } else {
- self.tableView.tableHeaderView = self.searchController.searchBar;
- }
-}
-
-- (void)setShowsCarousel:(BOOL)showsCarousel {
- if (_showsCarousel == showsCarousel) return;
- _showsCarousel = showsCarousel;
-
- _carousel = ({
- __weak __typeof(self) weakSelf = self;
-
- FLEXScopeCarousel *carousel = [FLEXScopeCarousel new];
- carousel.selectedIndexChangedAction = ^(NSInteger idx) {
- __typeof(self) self = weakSelf;
- [self updateSearchResults:self.searchText];
- };
-
- self.tableView.tableHeaderView = carousel;
- [self.tableView layoutIfNeeded];
- // UITableView won't update the header size unless you reset the header view
- [carousel registerBlockForDynamicTypeChanges:^(FLEXScopeCarousel *carousel) {
- __typeof(self) self = weakSelf;
- self.tableView.tableHeaderView = carousel;
- [self.tableView layoutIfNeeded];
- }];
-
- carousel;
- });
-}
-
-- (NSInteger)selectedScope {
- if (self.searchController.searchBar.showsScopeBar) {
- return self.searchController.searchBar.selectedScopeButtonIndex;
- } else if (self.showsCarousel) {
- return self.carousel.selectedIndex;
- } else {
- return NSNotFound;
- }
-}
-
-- (NSString *)searchText {
- return self.searchController.searchBar.text;
-}
-
-- (void)setAutomaticallyShowsSearchBarCancelButton:(BOOL)autoShowCancel {
-#if FLEX_AT_LEAST_IOS13_SDK
- if (@available(iOS 13.0, *)) {
- self.searchController.automaticallyShowsCancelButton = autoShowCancel;
- } else {
- _automaticallyShowsSearchBarCancelButton = autoShowCancel;
- }
-#else
- _automaticallyShowsSearchBarCancelButton = autoShowCancel;
-#endif
-}
-
-- (void)updateSearchResults:(NSString *)newText { }
-
-- (void)onBackgroundQueue:(NSArray *(^)())backgroundBlock thenOnMainQueue:(void(^)(NSArray *))mainBlock {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- NSArray *items = backgroundBlock();
- dispatch_async(dispatch_get_main_queue(), ^{
- mainBlock(items);
- });
- });
-}
-
-#pragma mark - View Controller Lifecycle
-
-- (void)viewDidLoad {
- [super viewDidLoad];
-
- self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
-}
-
-- (void)viewWillAppear:(BOOL)animated {
- [super viewWillAppear:animated];
-
- // Make the search bar re-appear instead of hiding
- if (@available(iOS 11.0, *)) if (!self.hideSearchBarInitially) {
- self.navigationItem.hidesSearchBarWhenScrolling = NO;
- }
-}
-
-- (void)viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
-
- // Allow scrolling to collapse the search bar,
- // only if we don't want it pinned
- if (@available(iOS 11.0, *)) if (!self.hideSearchBarInitially) {
- self.navigationItem.hidesSearchBarWhenScrolling = !self.pinSearchBar;
- }
-}
-
-- (void)viewDidDisappear:(BOOL)animated {
- [super viewDidDisappear:animated];
-
- if (self.searchController.active) {
- self.searchController.active = NO;
- }
-}
-
-#pragma mark - Private
-
-- (void)debounce:(void(^)())block {
- [self.debounceTimer invalidate];
-
- self.debounceTimer = [NSTimer
- scheduledTimerWithTimeInterval:self.searchBarDebounceInterval
- target:block
- selector:@selector(invoke)
- userInfo:nil
- repeats:NO
- ];
-}
-
-#pragma mark - Search Bar
-
-#pragma mark UISearchResultsUpdating
-
-- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
-{
- [self.debounceTimer invalidate];
- NSString *text = searchController.searchBar.text;
-
- // Only debounce if we want to, and if we have a non-empty string
- // Empty string events are sent instantly
- if (text.length && self.searchBarDebounceInterval > kFLEXDebounceInstant) {
- [self debounce:^{
- [self updateSearchResults:text];
- }];
- } else {
- [self updateSearchResults:text];
- }
-}
-
-#pragma mark UISearchControllerDelegate
-
-- (void)willPresentSearchController:(UISearchController *)searchController {
- if (self.automaticallyShowsSearchBarCancelButton) {
- [searchController.searchBar setShowsCancelButton:YES animated:YES];
- }
-}
-
-- (void)willDismissSearchController:(UISearchController *)searchController {
- if (self.automaticallyShowsSearchBarCancelButton) {
- [searchController.searchBar setShowsCancelButton:NO animated:YES];
- }
-}
-
-#pragma mark UISearchBarDelegate
-
-/// Not necessary in iOS 13; remove this when iOS 13 is the deployment target
-- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope {
- [self updateSearchResultsForSearchController:self.searchController];
-}
-
-@end
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXTableViewSection.h b/xcode/Pods/FLEX/Classes/Core/FLEXTableViewSection.h
index a2866e4a..f7959cea 100644
--- a/xcode/Pods/FLEX/Classes/Core/FLEXTableViewSection.h
+++ b/xcode/Pods/FLEX/Classes/Core/FLEXTableViewSection.h
@@ -2,38 +2,133 @@
// FLEXTableViewSection.h
// FLEX
//
-// Created by Tanner Bennett on 7/11/19.
-// Copyright © 2019 Flipboard. All rights reserved.
+// Created by Tanner on 1/29/20.
+// Copyright © 2020 Flipboard. All rights reserved.
//
-#import
+#import
+#import "FLEXMacros.h"
+#import "NSArray+FLEX.h"
+@class FLEXTableView;
NS_ASSUME_NONNULL_BEGIN
-/// A protocol for arbitrary case-insensitive pattern matching
-@protocol FLEXPatternMatching
-/// @return YES if the receiver matches the query, case-insensitive
-- (BOOL)matches:(NSString *)query;
-@end
+#pragma mark FLEXTableViewSection
-@interface FLEXTableViewSection<__covariant ObjectType> : NSObject
+/// An abstract base class for table view sections.
+///
+/// Many properties or methods here return nil or some logical equivalent by default.
+/// Even so, most of the methods with defaults are intended to be overriden by subclasses.
+/// Some methods are not implemented at all and MUST be implemented by a subclass.
+@interface FLEXTableViewSection : NSObject {
+ @protected
+ /// Unused by default, use if you want
+ NSString *_title;
+}
-+ (instancetype)section:(NSInteger)section title:(NSString *)title rows:(NSArray> *)rows;
+#pragma mark - Data
-@property (nonatomic, readonly) NSInteger section;
-@property (nonatomic, readonly) NSString *title;
-@property (nonatomic, readonly) NSArray> *rows;
+/// A title to be displayed for the custom section.
+/// Subclasses may override or use the \c _title ivar.
+@property (nonatomic, readonly, nullable) NSString *title;
+/// The number of rows in this section. Subclasses must override.
+/// This should not change until \c filterText is changed or \c reloadData is called.
+@property (nonatomic, readonly) NSInteger numberOfRows;
+/// A map of reuse identifiers to \c UITableViewCell (sub)class objects.
+/// Subclasses \e may override this as necessary, but are not required to.
+/// See \c FLEXTableView.h for more information.
+/// @return nil by default.
+@property (nonatomic, readonly, nullable) NSDictionary *cellRegistrationMapping;
-@property (nonatomic, readonly) NSInteger count;
+/// The section should filter itself based on the contents of this property
+/// as it is set. If it is set to nil or an empty string, it should not filter.
+/// Subclasses should override or observe this property and react to changes.
+///
+/// It is common practice to use two arrays for the underlying model:
+/// One to hold all rows, and one to hold unfiltered rows. When \c setFilterText:
+/// is called, call \c super to store the new value, and re-filter your model accordingly.
+@property (nonatomic, nullable) NSString *filterText;
-/// @return A new section containing only rows that match the string,
-/// or nil if the section was empty and no rows matched the string.
-- (nullable instancetype)newSectionWithRowsMatchingQuery:(NSString *)query;
+/// Provides an avenue for the section to refresh data or change the number of rows.
+///
+/// This is called before reloading the table view itself. If your section pulls data
+/// from an external data source, this is a good place to refresh that data entirely.
+/// If your section does not, then it might be simpler for you to just override
+/// \c setFilterText: to call \c super and call \c reloadData.
+- (void)reloadData;
-@end
+#pragma mark - Row Selection
+
+/// Whether the given row should be selectable, such as if tapping the cell
+/// should take the user to a new screen or trigger an action.
+/// Subclasses \e may override this as necessary, but are not required to.
+/// @return \c NO by default
+- (BOOL)canSelectRow:(NSInteger)row;
+
+/// An action "future" to be triggered when the row is selected, if the row
+/// supports being selected as indicated by \c canSelectRow:. Subclasses
+/// must implement this in accordance with how they implement \c canSelectRow:
+/// if they do not implement \c viewControllerToPushForRow:
+/// @return This returns \c nil if no view controller is provided by
+/// \c viewControllerToPushForRow: — otherwise it pushes that view controller
+/// onto \c host.navigationController
+- (nullable void(^)(__kindof UIViewController *host))didSelectRowAction:(NSInteger)row;
+
+/// A view controller to display when the row is selected, if the row
+/// supports being selected as indicated by \c canSelectRow:. Subclasses
+/// must implement this in accordance with how they implement \c canSelectRow:
+/// if they do not implement \c didSelectRowAction:
+/// @return \c nil by default
+- (nullable UIViewController *)viewControllerToPushForRow:(NSInteger)row;
+
+/// Called when the accessory view's detail button is pressed.
+/// @return \c nil by default.
+- (nullable void(^)(__kindof UIViewController *host))didPressInfoButtonAction:(NSInteger)row;
+
+#pragma mark - Context Menus
+#if FLEX_AT_LEAST_IOS13_SDK
+
+/// By default, this is the title of the row.
+/// @return The title of the context menu, if any.
+- (nullable NSString *)menuTitleForRow:(NSInteger)row API_AVAILABLE(ios(13.0));
+/// Protected, not intended for public use. \c menuTitleForRow:
+/// already includes the value returned from this method.
+///
+/// By default, this returns \c @"". Subclasses may override to
+/// provide a detailed description of the target of the context menu.
+- (NSString *)menuSubtitleForRow:(NSInteger)row API_AVAILABLE(ios(13.0));
+/// The context menu items, if any. Subclasses may override.
+/// By default, only inludes items for \c copyMenuItemsForRow:.
+- (nullable NSArray *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender API_AVAILABLE(ios(13.0));
+/// Subclasses may override to return a list of copiable items.
+///
+/// Every two elements in the list compose a key-value pair, where the key
+/// should be a description of what will be copied, and the values should be
+/// the strings to copy. Return an empty string as a value to show a disabled action.
+- (nullable NSArray *)copyMenuItemsForRow:(NSInteger)row API_AVAILABLE(ios(13.0));
+#endif
+
+#pragma mark - Cell Configuration
+
+/// Provide a reuse identifier for the given row. Subclasses should override.
+///
+/// Custom reuse identifiers should be specified in \c cellRegistrationMapping.
+/// You may return any of the identifiers in \c FLEXTableView.h
+/// without including them in the \c cellRegistrationMapping.
+/// @return \c kFLEXDefaultCell by default.
+- (NSString *)reuseIdentifierForRow:(NSInteger)row;
+/// Configure a cell for the given row. Subclasses must override.
+- (void)configureCell:(__kindof UITableViewCell *)cell forRow:(NSInteger)row;
+
+#pragma mark - External Convenience
+
+/// For use by whatever view controller uses your section. Not required.
+/// @return An optional title.
+- (nullable NSString *)titleForRow:(NSInteger)row;
+/// For use by whatever view controller uses your section. Not required.
+/// @return An optional subtitle.
+- (nullable NSString *)subtitleForRow:(NSInteger)row;
-@interface FLEXTableViewSection<__covariant ObjectType> (Subscripting)
-- (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx;
@end
NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXTableViewSection.m b/xcode/Pods/FLEX/Classes/Core/FLEXTableViewSection.m
index b1cf4c19..9090f99e 100644
--- a/xcode/Pods/FLEX/Classes/Core/FLEXTableViewSection.m
+++ b/xcode/Pods/FLEX/Classes/Core/FLEXTableViewSection.m
@@ -2,48 +2,127 @@
// FLEXTableViewSection.m
// FLEX
//
-// Created by Tanner Bennett on 7/11/19.
-// Copyright © 2019 Flipboard. All rights reserved.
+// Created by Tanner on 1/29/20.
+// Copyright © 2020 Flipboard. All rights reserved.
//
#import "FLEXTableViewSection.h"
+#import "FLEXTableView.h"
+#import "FLEXUtility.h"
+#import "UIMenu+FLEX.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wincomplete-implementation"
@implementation FLEXTableViewSection
-+ (instancetype)section:(NSInteger)section title:(NSString *)title rows:(NSArray *)rows {
- FLEXTableViewSection *s = [self new];
- s->_section = section;
- s->_title = title;
- s->_rows = rows.copy;
+- (NSInteger)numberOfRows {
+ return 0;
+}
+
+- (void)reloadData { }
+
+- (NSDictionary *)cellRegistrationMapping {
+ return nil;
+}
+
+- (BOOL)canSelectRow:(NSInteger)row { return NO; }
+
+- (void (^)(__kindof UIViewController *))didSelectRowAction:(NSInteger)row {
+ UIViewController *toPush = [self viewControllerToPushForRow:row];
+ if (toPush) {
+ return ^(UIViewController *host) {
+ [host.navigationController pushViewController:toPush animated:YES];
+ };
+ }
+
+ return nil;
+}
+
+- (UIViewController *)viewControllerToPushForRow:(NSInteger)row {
+ return nil;
+}
+
+- (void (^)(__kindof UIViewController *))didPressInfoButtonAction:(NSInteger)row {
+ return nil;
+}
- return s;
+- (NSString *)reuseIdentifierForRow:(NSInteger)row {
+ return kFLEXDefaultCell;
}
-- (instancetype)newSectionWithRowsMatchingQuery:(NSString *)query {
- // Find rows containing the search string
- NSPredicate *containsString = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *bindings) {
- return [obj matches:query];
- }];
- NSArray *filteredRows = [self.rows filteredArrayUsingPredicate:containsString];
+#if FLEX_AT_LEAST_IOS13_SDK
+
+- (NSString *)menuTitleForRow:(NSInteger)row {
+ NSString *title = [self titleForRow:row];
+ NSString *subtitle = [self menuSubtitleForRow:row];
- // Only return new section if not empty
- if (filteredRows.count) {
- return [[self class] section:self.section title:self.title rows:filteredRows];
+ if (subtitle.length) {
+ return [NSString stringWithFormat:@"%@\n\n%@", title, subtitle];
}
- return nil;
+ return title;
}
-- (NSInteger)count {
- return self.rows.count;
+- (NSString *)menuSubtitleForRow:(NSInteger)row {
+ return @"";
}
-@end
+- (NSArray *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender API_AVAILABLE(ios(13)) {
+ NSArray *copyItems = [self copyMenuItemsForRow:row];
+ NSAssert(copyItems.count % 2 == 0, @"copyMenuItemsForRow: should return an even list");
+
+ if (copyItems.count) {
+ NSInteger numberOfActions = copyItems.count / 2;
+ BOOL collapseMenu = numberOfActions > 4;
+ UIImage *copyIcon = [UIImage systemImageNamed:@"doc.on.doc"];
+
+ NSMutableArray *actions = [NSMutableArray new];
+
+ for (NSInteger i = 0; i < copyItems.count; i += 2) {
+ NSString *key = copyItems[i], *value = copyItems[i+1];
+ NSString *title = collapseMenu ? key : [@"Copy " stringByAppendingString:key];
+
+ UIAction *copy = [UIAction
+ actionWithTitle:title
+ image:copyIcon
+ identifier:nil
+ handler:^(__kindof UIAction *action) {
+ UIPasteboard.generalPasteboard.string = value;
+ }
+ ];
+ if (!value.length) {
+ copy.attributes = UIMenuElementAttributesDisabled;
+ }
+
+ [actions addObject:copy];
+ }
+
+ UIMenu *copyMenu = [UIMenu
+ inlineMenuWithTitle:@"Copy…"
+ image:copyIcon
+ children:actions
+ ];
+
+ if (collapseMenu) {
+ return @[[copyMenu collapsed]];
+ } else {
+ return @[copyMenu];
+ }
+ }
+
+ return @[];
+}
-@implementation FLEXTableViewSection (Subscripting)
+#endif
-- (id)objectAtIndexedSubscript:(NSUInteger)idx {
- return self.rows[idx];
+- (NSArray *)copyMenuItemsForRow:(NSInteger)row {
+ return nil;
}
+- (NSString *)titleForRow:(NSInteger)row { return nil; }
+- (NSString *)subtitleForRow:(NSInteger)row { return nil; }
+
@end
+
+#pragma clang diagnostic pop
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXCarouselCell.h b/xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXCarouselCell.h
similarity index 100%
rename from xcode/Pods/FLEX/Classes/Core/FLEXCarouselCell.h
rename to xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXCarouselCell.h
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXCarouselCell.m b/xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXCarouselCell.m
similarity index 91%
rename from xcode/Pods/FLEX/Classes/Core/FLEXCarouselCell.m
rename to xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXCarouselCell.m
index 062540ae..6965a098 100644
--- a/xcode/Pods/FLEX/Classes/Core/FLEXCarouselCell.m
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXCarouselCell.m
@@ -8,7 +8,7 @@
#import "FLEXCarouselCell.h"
#import "FLEXColor.h"
-#import "UIView+Layout.h"
+#import "UIView+FLEX_Layout.h"
@interface FLEXCarouselCell ()
@property (nonatomic, readonly) UILabel *titleLabel;
@@ -25,8 +25,10 @@ - (instancetype)initWithFrame:(CGRect)frame {
_selectionIndicatorStripe = [UIView new];
self.titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
- self.titleLabel.adjustsFontForContentSizeCategory = YES;
self.selectionIndicatorStripe.backgroundColor = self.tintColor;
+ if (@available(iOS 10, *)) {
+ self.titleLabel.adjustsFontForContentSizeCategory = YES;
+ }
[self.contentView addSubview:self.titleLabel];
[self.contentView addSubview:self.selectionIndicatorStripe];
@@ -45,7 +47,7 @@ - (void)updateAppearance {
if (self.selected) {
self.titleLabel.textColor = self.tintColor;
} else {
- self.titleLabel.textColor = [FLEXColor deemphasizedTextColor];
+ self.titleLabel.textColor = FLEXColor.deemphasizedTextColor;
}
}
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXScopeCarousel.h b/xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXScopeCarousel.h
similarity index 100%
rename from xcode/Pods/FLEX/Classes/Core/FLEXScopeCarousel.h
rename to xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXScopeCarousel.h
diff --git a/xcode/Pods/FLEX/Classes/Core/FLEXScopeCarousel.m b/xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXScopeCarousel.m
similarity index 85%
rename from xcode/Pods/FLEX/Classes/Core/FLEXScopeCarousel.m
rename to xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXScopeCarousel.m
index 43bbdb26..c96663eb 100644
--- a/xcode/Pods/FLEX/Classes/Core/FLEXScopeCarousel.m
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Carousel/FLEXScopeCarousel.m
@@ -9,7 +9,7 @@
#import "FLEXScopeCarousel.h"
#import "FLEXCarouselCell.h"
#import "FLEXColor.h"
-#import "UIView+Layout.h"
+#import "UIView+FLEX_Layout.h"
const CGFloat kCarouselItemSpacing = 0;
NSString * const kCarouselCellReuseIdentifier = @"kCarouselCellReuseIdentifier";
@@ -17,7 +17,6 @@
@interface FLEXScopeCarousel ()
@property (nonatomic, readonly) UICollectionView *collectionView;
@property (nonatomic, readonly) FLEXCarouselCell *sizingCell;
-@property (nonatomic, readonly) NSLayoutConstraint *heightConstraint;
@property (nonatomic, readonly) id dynamicTypeObserver;
@property (nonatomic, readonly) NSMutableArray *dynamicTypeHandlers;
@@ -30,9 +29,15 @@ @implementation FLEXScopeCarousel
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
- self.backgroundColor = [FLEXColor primaryBackgroundColor];
+ self.backgroundColor = FLEXColor.primaryBackgroundColor;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+ self.translatesAutoresizingMaskIntoConstraints = YES;
_dynamicTypeHandlers = [NSMutableArray new];
+
+ CGSize itemSize = CGSizeZero;
+ if (@available(iOS 10.0, *)) {
+ itemSize = UICollectionViewFlowLayoutAutomaticSize;
+ }
// Collection view layout
UICollectionViewFlowLayout *layout = ({
@@ -40,8 +45,8 @@ - (id)initWithFrame:(CGRect)frame {
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.sectionInset = UIEdgeInsetsZero;
layout.minimumLineSpacing = kCarouselItemSpacing;
- layout.itemSize = UICollectionViewFlowLayoutAutomaticSize;
- layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize;
+ layout.itemSize = itemSize;
+ layout.estimatedItemSize = itemSize;
layout;
});
@@ -76,7 +81,7 @@ - (id)initWithFrame:(CGRect)frame {
// Notify observers
__typeof(self) self = weakSelf;
- for (void (^block)() in self.dynamicTypeHandlers) {
+ for (void (^block)(FLEXScopeCarousel *) in self.dynamicTypeHandlers) {
block(self);
}
}
@@ -99,7 +104,7 @@ - (void)drawRect:(CGRect)rect {
// Draw hairline
CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSetStrokeColorWithColor(context, [FLEXColor hairlineColor].CGColor);
+ CGContextSetStrokeColorWithColor(context, FLEXColor.hairlineColor.CGColor);
CGContextSetLineWidth(context, width);
CGContextMoveToPoint(context, 0, rect.size.height - width);
CGContextAddLineToPoint(context, rect.size.width, rect.size.height - width);
@@ -112,25 +117,22 @@ + (BOOL)requiresConstraintBasedLayout {
- (void)updateConstraints {
if (!self.constraintsInstalled) {
- self.translatesAutoresizingMaskIntoConstraints = NO;
self.collectionView.translatesAutoresizingMaskIntoConstraints = NO;
-
- [self.centerXAnchor constraintEqualToAnchor:self.superview.centerXAnchor].active = YES;
- [self.widthAnchor constraintEqualToAnchor:self.superview.widthAnchor].active = YES;
- [self.topAnchor constraintEqualToAnchor:self.superview.topAnchor].active = YES;
-
[self.collectionView pinEdgesToSuperview];
- _heightConstraint = [self.heightAnchor constraintEqualToConstant:100];
- self.heightConstraint.active = YES;
-
+
self.constraintsInstalled = YES;
}
-
- self.heightConstraint.constant = [self.sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
[super updateConstraints];
}
+- (CGSize)intrinsicContentSize {
+ return CGSizeMake(
+ UIViewNoIntrinsicMetric,
+ [self.sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height
+ );
+}
+
#pragma mark - Public
- (void)setItems:(NSArray *)items {
@@ -161,6 +163,10 @@ - (void)registerBlockForDynamicTypeChanges:(void (^)(FLEXScopeCarousel *))handle
#pragma mark - UICollectionView
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
+// if (@available(iOS 10.0, *)) {
+// return UICollectionViewFlowLayoutAutomaticSize;
+// }
+
self.sizingCell.title = self.items[indexPath.item];
return [self.sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
}
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXCodeFontCell.h b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXCodeFontCell.h
new file mode 100644
index 00000000..8be8b217
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXCodeFontCell.h
@@ -0,0 +1,17 @@
+//
+// FLEXCodeFontCell.h
+// FLEX
+//
+// Created by Tanner on 12/27/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import "FLEXMultilineTableViewCell.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FLEXCodeFontCell : FLEXMultilineDetailTableViewCell
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXCodeFontCell.m b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXCodeFontCell.m
new file mode 100644
index 00000000..ab7272e8
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXCodeFontCell.m
@@ -0,0 +1,34 @@
+//
+// FLEXCodeFontCell.m
+// FLEX
+//
+// Created by Tanner on 12/27/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import "FLEXCodeFontCell.h"
+#import "UIFont+FLEX.h"
+
+@implementation FLEXCodeFontCell
+
+- (void)postInit {
+ [super postInit];
+
+ self.titleLabel.font = UIFont.flex_codeFont;
+ self.subtitleLabel.font = UIFont.flex_codeFont;
+
+ self.titleLabel.adjustsFontSizeToFitWidth = YES;
+ self.titleLabel.minimumScaleFactor = 0.9;
+ self.subtitleLabel.adjustsFontSizeToFitWidth = YES;
+ self.subtitleLabel.minimumScaleFactor = 0.75;
+
+ // Disable mutli-line pre iOS 11
+ if (@available(iOS 11, *)) {
+ self.subtitleLabel.numberOfLines = 5;
+ } else {
+ self.titleLabel.numberOfLines = 1;
+ self.subtitleLabel.numberOfLines = 1;
+ }
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXKeyValueTableViewCell.h b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXKeyValueTableViewCell.h
new file mode 100644
index 00000000..c04dbaf9
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXKeyValueTableViewCell.h
@@ -0,0 +1,13 @@
+//
+// FLEXKeyValueTableViewCell.h
+// FLEX
+//
+// Created by Tanner Bennett on 1/23/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableViewCell.h"
+
+@interface FLEXKeyValueTableViewCell : FLEXTableViewCell
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXKeyValueTableViewCell.m b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXKeyValueTableViewCell.m
new file mode 100644
index 00000000..f869d46e
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXKeyValueTableViewCell.m
@@ -0,0 +1,17 @@
+//
+// FLEXKeyValueTableViewCell.m
+// FLEX
+//
+// Created by Tanner Bennett on 1/23/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXKeyValueTableViewCell.h"
+
+@implementation FLEXKeyValueTableViewCell
+
+- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
+ return [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXMultilineTableViewCell.h b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXMultilineTableViewCell.h
new file mode 100644
index 00000000..acdf9d4a
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXMultilineTableViewCell.h
@@ -0,0 +1,24 @@
+//
+// FLEXMultilineTableViewCell.h
+// FLEX
+//
+// Created by Ryan Olson on 2/13/15.
+// Copyright (c) 2015 f. All rights reserved.
+//
+
+#import "FLEXTableViewCell.h"
+
+/// A cell with both labels set to be multi-line capable.
+@interface FLEXMultilineTableViewCell : FLEXTableViewCell
+
++ (CGFloat)preferredHeightWithAttributedText:(NSAttributedString *)attributedText
+ maxWidth:(CGFloat)contentViewWidth
+ style:(UITableViewStyle)style
+ showsAccessory:(BOOL)showsAccessory;
+
+@end
+
+/// A \c FLEXMultilineTableViewCell initialized with \c UITableViewCellStyleSubtitle
+@interface FLEXMultilineDetailTableViewCell : FLEXMultilineTableViewCell
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXMultilineTableViewCell.m b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXMultilineTableViewCell.m
new file mode 100644
index 00000000..c55ee754
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXMultilineTableViewCell.m
@@ -0,0 +1,67 @@
+//
+// FLEXMultilineTableViewCell.m
+// FLEX
+//
+// Created by Ryan Olson on 2/13/15.
+// Copyright (c) 2015 f. All rights reserved.
+//
+
+#import "FLEXMultilineTableViewCell.h"
+#import "UIView+FLEX_Layout.h"
+#import "FLEXUtility.h"
+
+@interface FLEXMultilineTableViewCell ()
+@property (nonatomic, readonly) UILabel *_titleLabel;
+@property (nonatomic, readonly) UILabel *_subtitleLabel;
+@property (nonatomic) BOOL constraintsUpdated;
+@end
+
+@implementation FLEXMultilineTableViewCell
+
+- (void)postInit {
+ [super postInit];
+
+ self.titleLabel.numberOfLines = 0;
+ self.subtitleLabel.numberOfLines = 0;
+}
+
++ (UIEdgeInsets)labelInsets {
+ return UIEdgeInsetsMake(10.0, 16.0, 10.0, 8.0);
+}
+
++ (CGFloat)preferredHeightWithAttributedText:(NSAttributedString *)attributedText
+ maxWidth:(CGFloat)contentViewWidth
+ style:(UITableViewStyle)style
+ showsAccessory:(BOOL)showsAccessory {
+ CGFloat labelWidth = contentViewWidth;
+
+ // Content view inset due to accessory view observed on iOS 8.1 iPhone 6.
+ if (showsAccessory) {
+ labelWidth -= 34.0;
+ }
+
+ UIEdgeInsets labelInsets = [self labelInsets];
+ labelWidth -= (labelInsets.left + labelInsets.right);
+
+ CGSize constrainSize = CGSizeMake(labelWidth, CGFLOAT_MAX);
+ CGRect boundingBox = [attributedText
+ boundingRectWithSize:constrainSize
+ options:NSStringDrawingUsesLineFragmentOrigin
+ context:nil
+ ];
+ CGFloat preferredLabelHeight = FLEXFloor(boundingBox.size.height);
+ CGFloat preferredCellHeight = preferredLabelHeight + labelInsets.top + labelInsets.bottom + 1.0;
+
+ return preferredCellHeight;
+}
+
+@end
+
+
+@implementation FLEXMultilineDetailTableViewCell
+
+- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
+ return [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXSubtitleTableViewCell.h b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXSubtitleTableViewCell.h
new file mode 100644
index 00000000..e835012f
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXSubtitleTableViewCell.h
@@ -0,0 +1,14 @@
+//
+// FLEXSubtitleTableViewCell.h
+// FLEX
+//
+// Created by Tanner on 4/17/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableViewCell.h"
+
+/// A cell initialized with \c UITableViewCellStyleSubtitle
+@interface FLEXSubtitleTableViewCell : FLEXTableViewCell
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/ObjectExplorers/Views/FLEXSubtitleTableViewCell.m b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXSubtitleTableViewCell.m
similarity index 92%
rename from xcode/Pods/FLEX/Classes/ObjectExplorers/Views/FLEXSubtitleTableViewCell.m
rename to xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXSubtitleTableViewCell.m
index fc09db50..e487b614 100644
--- a/xcode/Pods/FLEX/Classes/ObjectExplorers/Views/FLEXSubtitleTableViewCell.m
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXSubtitleTableViewCell.m
@@ -10,8 +10,7 @@
@implementation FLEXSubtitleTableViewCell
-- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
-{
+- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
return [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
}
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXTableViewCell.h b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXTableViewCell.h
new file mode 100644
index 00000000..ab43760b
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXTableViewCell.h
@@ -0,0 +1,23 @@
+//
+// FLEXTableViewCell.h
+// FLEX
+//
+// Created by Tanner on 4/17/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import
+
+@interface FLEXTableViewCell : UITableViewCell
+
+/// Use this instead of .textLabel
+@property (nonatomic, readonly) UILabel *titleLabel;
+/// Use this instead of .detailTextLabel
+@property (nonatomic, readonly) UILabel *subtitleLabel;
+
+/// Subclasses can override this instead of initializers to
+/// perform additional initialization without lots of boilerplate.
+/// Remember to call super!
+- (void)postInit;
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXTableViewCell.m b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXTableViewCell.m
new file mode 100644
index 00000000..56deaf33
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/Cells/FLEXTableViewCell.m
@@ -0,0 +1,57 @@
+//
+// FLEXTableViewCell.m
+// FLEX
+//
+// Created by Tanner on 4/17/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableViewCell.h"
+#import "FLEXUtility.h"
+#import "FLEXColor.h"
+#import "FLEXTableView.h"
+
+@interface UITableView (Internal)
+// Exists at least since iOS 5
+- (BOOL)_canPerformAction:(SEL)action forCell:(UITableViewCell *)cell sender:(id)sender;
+- (void)_performAction:(SEL)action forCell:(UITableViewCell *)cell sender:(id)sender;
+@end
+
+@interface UITableViewCell (Internal)
+// Exists at least since iOS 5
+@property (nonatomic, readonly) FLEXTableView *_tableView;
+@end
+
+@implementation FLEXTableViewCell
+
+- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
+ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
+ if (self) {
+ [self postInit];
+ }
+
+ return self;
+}
+
+- (void)postInit {
+ UIFont *cellFont = UIFont.flex_defaultTableCellFont;
+ self.titleLabel.font = cellFont;
+ self.subtitleLabel.font = cellFont;
+ self.subtitleLabel.textColor = FLEXColor.deemphasizedTextColor;
+
+ self.titleLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
+ self.subtitleLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
+
+ self.titleLabel.numberOfLines = 1;
+ self.subtitleLabel.numberOfLines = 1;
+}
+
+- (UILabel *)titleLabel {
+ return self.textLabel;
+}
+
+- (UILabel *)subtitleLabel {
+ return self.detailTextLabel;
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/FLEXTableView.h b/xcode/Pods/FLEX/Classes/Core/Views/FLEXTableView.h
new file mode 100644
index 00000000..a750c0b5
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/FLEXTableView.h
@@ -0,0 +1,48 @@
+//
+// FLEXTableView.h
+// FLEX
+//
+// Created by Tanner on 4/17/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark Reuse identifiers
+
+typedef NSString * FLEXTableViewCellReuseIdentifier;
+
+/// A regular \c FLEXTableViewCell initialized with \c UITableViewCellStyleDefault
+extern FLEXTableViewCellReuseIdentifier const kFLEXDefaultCell;
+/// A \c FLEXSubtitleTableViewCell initialized with \c UITableViewCellStyleSubtitle
+extern FLEXTableViewCellReuseIdentifier const kFLEXDetailCell;
+/// A \c FLEXMultilineTableViewCell initialized with \c UITableViewCellStyleDefault
+extern FLEXTableViewCellReuseIdentifier const kFLEXMultilineCell;
+/// A \c FLEXMultilineTableViewCell initialized with \c UITableViewCellStyleSubtitle
+extern FLEXTableViewCellReuseIdentifier const kFLEXMultilineDetailCell;
+/// A \c FLEXTableViewCell initialized with \c UITableViewCellStyleValue1
+extern FLEXTableViewCellReuseIdentifier const kFLEXKeyValueCell;
+/// A \c FLEXSubtitleTableViewCell which uses monospaced fonts for both labels
+extern FLEXTableViewCellReuseIdentifier const kFLEXCodeFontCell;
+
+#pragma mark - FLEXTableView
+@interface FLEXTableView : UITableView
+
++ (instancetype)flexDefaultTableView;
++ (instancetype)groupedTableView;
++ (instancetype)plainTableView;
++ (instancetype)style:(UITableViewStyle)style;
+
+/// You do not need to register classes for any of the default reuse identifiers above
+/// (annotated as \c FLEXTableViewCellReuseIdentifier types) unless you wish to provide
+/// a custom cell for any of those reuse identifiers. By default, \c FLEXTableViewCell,
+/// \c FLEXSubtitleTableViewCell, and \c FLEXMultilineTableViewCell are used, respectively.
+///
+/// @param registrationMapping A map of reuse identifiers to \c UITableViewCell (sub)class objects.
+- (void)registerCells:(NSDictionary *)registrationMapping;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/Core/Views/FLEXTableView.m b/xcode/Pods/FLEX/Classes/Core/Views/FLEXTableView.m
new file mode 100644
index 00000000..073f76ff
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Core/Views/FLEXTableView.m
@@ -0,0 +1,91 @@
+//
+// FLEXTableView.m
+// FLEX
+//
+// Created by Tanner on 4/17/19.
+// Copyright © 2019 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableView.h"
+#import "FLEXUtility.h"
+#import "FLEXSubtitleTableViewCell.h"
+#import "FLEXMultilineTableViewCell.h"
+#import "FLEXKeyValueTableViewCell.h"
+#import "FLEXCodeFontCell.h"
+
+FLEXTableViewCellReuseIdentifier const kFLEXDefaultCell = @"kFLEXDefaultCell";
+FLEXTableViewCellReuseIdentifier const kFLEXDetailCell = @"kFLEXDetailCell";
+FLEXTableViewCellReuseIdentifier const kFLEXMultilineCell = @"kFLEXMultilineCell";
+FLEXTableViewCellReuseIdentifier const kFLEXMultilineDetailCell = @"kFLEXMultilineDetailCell";
+FLEXTableViewCellReuseIdentifier const kFLEXKeyValueCell = @"kFLEXKeyValueCell";
+FLEXTableViewCellReuseIdentifier const kFLEXCodeFontCell = @"kFLEXCodeFontCell";
+
+#pragma mark Private
+
+@interface UITableView (Private)
+- (CGFloat)_heightForHeaderInSection:(NSInteger)section;
+- (NSString *)_titleForHeaderInSection:(NSInteger)section;
+@end
+
+@implementation FLEXTableView
+
++ (instancetype)flexDefaultTableView {
+#if FLEX_AT_LEAST_IOS13_SDK
+ if (@available(iOS 13.0, *)) {
+ return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
+ } else {
+ return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
+ }
+#else
+ return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
+#endif
+}
+
+#pragma mark - Initialization
+
++ (id)groupedTableView {
+#if FLEX_AT_LEAST_IOS13_SDK
+ if (@available(iOS 13.0, *)) {
+ return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
+ } else {
+ return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
+ }
+#else
+ return [[self alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
+#endif
+}
+
++ (id)plainTableView {
+ return [[self alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
+}
+
++ (id)style:(UITableViewStyle)style {
+ return [[self alloc] initWithFrame:CGRectZero style:style];
+}
+
+- (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
+ self = [super initWithFrame:frame style:style];
+ if (self) {
+ [self registerCells:@{
+ kFLEXDefaultCell : [FLEXTableViewCell class],
+ kFLEXDetailCell : [FLEXSubtitleTableViewCell class],
+ kFLEXMultilineCell : [FLEXMultilineTableViewCell class],
+ kFLEXMultilineDetailCell : [FLEXMultilineDetailTableViewCell class],
+ kFLEXKeyValueCell : [FLEXKeyValueTableViewCell class],
+ kFLEXCodeFontCell : [FLEXCodeFontCell class],
+ }];
+ }
+
+ return self;
+}
+
+
+#pragma mark - Public
+
+- (void)registerCells:(NSDictionary *)registrationMapping {
+ [registrationMapping enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, Class cellClass, BOOL *stop) {
+ [self registerClass:cellClass forCellReuseIdentifier:identifier];
+ }];
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.h
index ab3bd7b3..858528f3 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/30/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputView.h"
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.m
index 61f60456..f4ca604f 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputColorView.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/30/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputColorView.h"
@@ -30,18 +30,16 @@ - (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)col
@implementation FLEXColorComponentInputView
-- (id)initWithFrame:(CGRect)frame
-{
+- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.slider = [UISlider new];
- self.slider.backgroundColor = self.backgroundColor;
[self.slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
[self addSubview:self.slider];
self.valueLabel = [UILabel new];
self.valueLabel.backgroundColor = self.backgroundColor;
- self.valueLabel.font = [FLEXUtility defaultFontOfSize:14.0];
+ self.valueLabel.font = [UIFont systemFontOfSize:14.0];
self.valueLabel.textAlignment = NSTextAlignmentRight;
[self addSubview:self.valueLabel];
@@ -50,15 +48,13 @@ - (id)initWithFrame:(CGRect)frame
return self;
}
-- (void)setBackgroundColor:(UIColor *)backgroundColor
-{
+- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
self.slider.backgroundColor = backgroundColor;
self.valueLabel.backgroundColor = backgroundColor;
}
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
[super layoutSubviews];
const CGFloat kValueLabelWidth = 50.0;
@@ -73,19 +69,16 @@ - (void)layoutSubviews
self.valueLabel.frame = CGRectMake(valueLabelOriginX, valueLabelOriginY, kValueLabelWidth, self.valueLabel.frame.size.height);
}
-- (void)sliderChanged:(id)sender
-{
+- (void)sliderChanged:(id)sender {
[self.delegate colorComponentInputViewValueDidChange:self];
[self updateValueLabel];
}
-- (void)updateValueLabel
-{
+- (void)updateValueLabel {
self.valueLabel.text = [NSString stringWithFormat:@"%.3f", self.slider.value];
}
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGFloat height = [self.slider sizeThatFits:size].height;
return CGSizeMake(size.width, height);
}
@@ -102,8 +95,7 @@ @interface FLEXColorPreviewBox : UIView
@implementation FLEXColorPreviewBox
-- (id)initWithFrame:(CGRect)frame
-{
+- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.layer.borderWidth = 1.0;
@@ -118,18 +110,15 @@ - (id)initWithFrame:(CGRect)frame
return self;
}
-- (void)setColor:(UIColor *)color
-{
+- (void)setColor:(UIColor *)color {
self.colorOverlayView.backgroundColor = color;
}
-- (UIColor *)color
-{
+- (UIColor *)color {
return self.colorOverlayView.backgroundColor;
}
-+ (UIImage *)backgroundPatternImage
-{
++ (UIImage *)backgroundPatternImage {
const CGFloat kSquareDimension = 5.0;
CGSize squareSize = CGSizeMake(kSquareDimension, kSquareDimension);
CGSize imageSize = CGSizeMake(2.0 * kSquareDimension, 2.0 * kSquareDimension);
@@ -164,8 +153,7 @@ @interface FLEXArgumentInputColorView ()
@implementation FLEXArgumentInputColorView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
self.colorPreviewBox = [FLEXColorPreviewBox new];
@@ -174,7 +162,7 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
self.hexLabel = [UILabel new];
self.hexLabel.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.9];
self.hexLabel.textAlignment = NSTextAlignmentCenter;
- self.hexLabel.font = [FLEXUtility defaultFontOfSize:12.0];
+ self.hexLabel.font = [UIFont systemFontOfSize:12.0];
[self addSubview:self.hexLabel];
self.alphaInput = [FLEXColorComponentInputView new];
@@ -200,8 +188,7 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
return self;
}
-- (void)setBackgroundColor:(UIColor *)backgroundColor
-{
+- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
self.alphaInput.backgroundColor = backgroundColor;
self.redInput.backgroundColor = backgroundColor;
@@ -209,8 +196,7 @@ - (void)setBackgroundColor:(UIColor *)backgroundColor
self.blueInput.backgroundColor = backgroundColor;
}
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
[super layoutSubviews];
CGFloat runningOriginY = 0;
@@ -236,8 +222,7 @@ - (void)layoutSubviews
}
}
-- (void)setInputValue:(id)inputValue
-{
+- (void)setInputValue:(id)inputValue {
if ([inputValue isKindOfClass:[UIColor class]]) {
[self updateWithColor:inputValue];
} else if ([inputValue isKindOfClass:[NSValue class]]) {
@@ -248,21 +233,20 @@ - (void)setInputValue:(id)inputValue
UIColor *color = [[UIColor alloc] initWithCGColor:colorRef];
[self updateWithColor:color];
}
+ } else {
+ [self updateWithColor:UIColor.clearColor];
}
}
-- (id)inputValue
-{
+- (id)inputValue {
return [UIColor colorWithRed:self.redInput.slider.value green:self.greenInput.slider.value blue:self.blueInput.slider.value alpha:self.alphaInput.slider.value];
}
-- (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView
-{
+- (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView {
[self updateColorPreview];
}
-- (void)updateWithColor:(UIColor *)color
-{
+- (void)updateWithColor:(UIColor *)color {
CGFloat red, green, blue, white, alpha;
if ([color getRed:&red green:&green blue:&blue alpha:&alpha]) {
self.alphaInput.slider.value = alpha;
@@ -286,8 +270,7 @@ - (void)updateWithColor:(UIColor *)color
[self updateColorPreview];
}
-- (void)updateColorPreview
-{
+- (void)updateColorPreview {
self.colorPreviewBox.color = self.inputValue;
unsigned char redByte = self.redInput.slider.value * 255;
unsigned char greenByte = self.greenInput.slider.value * 255;
@@ -296,8 +279,7 @@ - (void)updateColorPreview
[self setNeedsLayout];
}
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGFloat height = 0;
height += [[self class] colorPreviewBoxHeight];
height += [[self class] inputViewVerticalPadding];
@@ -311,19 +293,19 @@ - (CGSize)sizeThatFits:(CGSize)size
return CGSizeMake(size.width, height);
}
-+ (CGFloat)inputViewVerticalPadding
-{
++ (CGFloat)inputViewVerticalPadding {
return 10.0;
}
-+ (CGFloat)colorPreviewBoxHeight
-{
++ (CGFloat)colorPreviewBoxHeight {
return 40.0;
}
-+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
-{
- return (type && (strcmp(type, @encode(CGColorRef)) == 0 || strcmp(type, FLEXEncodeClass(UIColor)) == 0)) || [value isKindOfClass:[UIColor class]];
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
+ NSParameterAssert(type);
+
+ // We don't care if currentValue is a color or not; we will default to +clearColor
+ return (strcmp(type, @encode(CGColorRef)) == 0) || (strcmp(type, FLEXEncodeClass(UIColor)) == 0);
}
@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.h
index 54e63a97..7566d02d 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Daniel Rodriguez Troitino on 2/14/15.
-// Copyright (c) 2015 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputView.h"
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.m
index 14471825..5e350d9f 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputDateView.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Daniel Rodriguez Troitino on 2/14/15.
-// Copyright (c) 2015 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputDateView.h"
@@ -17,8 +17,7 @@ @interface FLEXArgumentInputDateView ()
@implementation FLEXArgumentInputDateView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
self.datePicker = [UIDatePicker new];
@@ -31,33 +30,29 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
return self;
}
-- (void)setInputValue:(id)inputValue
-{
+- (void)setInputValue:(id)inputValue {
if ([inputValue isKindOfClass:[NSDate class]]) {
self.datePicker.date = inputValue;
}
}
-- (id)inputValue
-{
+- (id)inputValue {
return self.datePicker.date;
}
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
[super layoutSubviews];
self.datePicker.frame = self.bounds;
}
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGFloat height = [self.datePicker sizeThatFits:size].height;
return CGSizeMake(size.width, height);
}
-+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
-{
- return (type && (strcmp(type, FLEXEncodeClass(NSDate)) == 0)) || [value isKindOfClass:[NSDate class]];
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
+ NSParameterAssert(type);
+ return strcmp(type, FLEXEncodeClass(NSDate)) == 0;
}
@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.h
index 11847e60..b0ee9751 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/28/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputView.h"
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.m
index 9e13aeaf..42908621 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontView.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/28/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputFontView.h"
@@ -20,18 +20,15 @@ @interface FLEXArgumentInputFontView ()
@implementation FLEXArgumentInputFontView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
self.fontNameInput = [[FLEXArgumentInputFontsPickerView alloc] initWithArgumentTypeEncoding:FLEXEncodeClass(NSString)];
- self.fontNameInput.backgroundColor = self.backgroundColor;
self.fontNameInput.targetSize = FLEXArgumentInputViewSizeSmall;
self.fontNameInput.title = @"Font Name:";
[self addSubview:self.fontNameInput];
self.pointSizeInput = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:@encode(CGFloat)];
- self.pointSizeInput.backgroundColor = self.backgroundColor;
self.pointSizeInput.targetSize = FLEXArgumentInputViewSizeSmall;
self.pointSizeInput.title = @"Point Size:";
[self addSubview:self.pointSizeInput];
@@ -39,15 +36,13 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
return self;
}
-- (void)setBackgroundColor:(UIColor *)backgroundColor
-{
+- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
self.fontNameInput.backgroundColor = backgroundColor;
self.pointSizeInput.backgroundColor = backgroundColor;
}
-- (void)setInputValue:(id)inputValue
-{
+- (void)setInputValue:(id)inputValue {
if ([inputValue isKindOfClass:[UIFont class]]) {
UIFont *font = (UIFont *)inputValue;
self.fontNameInput.inputValue = font.fontName;
@@ -55,8 +50,7 @@ - (void)setInputValue:(id)inputValue
}
}
-- (id)inputValue
-{
+- (id)inputValue {
CGFloat pointSize = 0;
if ([self.pointSizeInput.inputValue isKindOfClass:[NSValue class]]) {
NSValue *pointSizeValue = (NSValue *)self.pointSizeInput.inputValue;
@@ -67,16 +61,14 @@ - (id)inputValue
return [UIFont fontWithName:self.fontNameInput.inputValue size:pointSize];
}
-- (BOOL)inputViewIsFirstResponder
-{
+- (BOOL)inputViewIsFirstResponder {
return [self.fontNameInput inputViewIsFirstResponder] || [self.pointSizeInput inputViewIsFirstResponder];
}
#pragma mark - Layout and Sizing
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
[super layoutSubviews];
CGFloat runningOriginY = self.topInputFieldVerticalLayoutGuide;
@@ -89,13 +81,11 @@ - (void)layoutSubviews
self.pointSizeInput.frame = CGRectMake(0, runningOriginY, pointSizeFitSize.width, pointSizeFitSize.height);
}
-+ (CGFloat)verticalPaddingBetweenFields
-{
++ (CGFloat)verticalPaddingBetweenFields {
return 10.0;
}
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGSize fitSize = [super sizeThatFits:size];
CGSize constrainSize = CGSizeMake(size.width, CGFLOAT_MAX);
@@ -111,11 +101,9 @@ - (CGSize)sizeThatFits:(CGSize)size
#pragma mark -
-+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
-{
- BOOL supported = type && strcmp(type, FLEXEncodeClass(UIFont)) == 0;
- supported = supported || (value && [value isKindOfClass:[UIFont class]]);
- return supported;
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
+ NSParameterAssert(type);
+ return strcmp(type, FLEXEncodeClass(UIFont)) == 0;
}
@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontsPickerView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontsPickerView.m
index 1b4a3e1f..f280be0f 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontsPickerView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputFontsPickerView.m
@@ -18,8 +18,7 @@ @interface FLEXArgumentInputFontsPickerView ()
@implementation FLEXArgumentInputFontsPickerView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
self.targetSize = FLEXArgumentInputViewSizeSmall;
@@ -29,8 +28,7 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
return self;
}
-- (void)setInputValue:(id)inputValue
-{
+- (void)setInputValue:(id)inputValue {
self.inputTextView.text = inputValue;
if ([self.availableFonts indexOfObject:inputValue] == NSNotFound) {
[self.availableFonts insertObject:inputValue atIndex:0];
@@ -38,15 +36,13 @@ - (void)setInputValue:(id)inputValue
[(UIPickerView *)self.inputTextView.inputView selectRow:[self.availableFonts indexOfObject:inputValue] inComponent:0 animated:NO];
}
-- (id)inputValue
-{
+- (id)inputValue {
return self.inputTextView.text.length > 0 ? [self.inputTextView.text copy] : nil;
}
#pragma mark - private
-- (UIPickerView*)createFontsPicker
-{
+- (UIPickerView*)createFontsPicker {
UIPickerView *fontsPicker = [UIPickerView new];
fontsPicker.dataSource = self;
fontsPicker.delegate = self;
@@ -54,10 +50,9 @@ - (UIPickerView*)createFontsPicker
return fontsPicker;
}
-- (void)createAvailableFonts
-{
- NSMutableArray *unsortedFontsArray = [NSMutableArray array];
- for (NSString *eachFontFamily in [UIFont familyNames]) {
+- (void)createAvailableFonts {
+ NSMutableArray *unsortedFontsArray = [NSMutableArray new];
+ for (NSString *eachFontFamily in UIFont.familyNames) {
for (NSString *eachFontName in [UIFont fontNamesForFamilyName:eachFontFamily]) {
[unsortedFontsArray addObject:eachFontName];
}
@@ -67,20 +62,17 @@ - (void)createAvailableFonts
#pragma mark - UIPickerViewDataSource
-- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
-{
+- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
-- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
-{
+- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return self.availableFonts.count;
}
#pragma mark - UIPickerViewDelegate
-- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
-{
+- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
UILabel *fontLabel;
if (!view) {
fontLabel = [UILabel new];
@@ -97,8 +89,7 @@ - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forC
return fontLabel;
}
-- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
-{
+- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
self.inputTextView.text = self.availableFonts[row];
}
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.h
index 77923efc..76105579 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/18/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputTextView.h"
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.m
index b4945756..2dc25e34 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNotSupportedView.m
@@ -3,20 +3,20 @@
// Flipboard
//
// Created by Ryan Olson on 6/18/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputNotSupportedView.h"
+#import "FLEXColor.h"
@implementation FLEXArgumentInputNotSupportedView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
self.inputTextView.userInteractionEnabled = NO;
- self.inputTextView.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1.0];
- self.inputTextView.text = @"nil";
+ self.inputTextView.backgroundColor = [FLEXColor secondaryGroupedBackgroundColorWithAlpha:0.5];
+ self.inputPlaceholderText = @"nil (type not supported)";
self.targetSize = FLEXArgumentInputViewSizeSmall;
}
return self;
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.h
index 754ea613..42d50b85 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/15/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputTextView.h"
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.m
index a6bc207c..b8689a7e 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputNumberView.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/15/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputNumberView.h"
@@ -11,48 +11,52 @@
@implementation FLEXArgumentInputNumberView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
self.inputTextView.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
self.targetSize = FLEXArgumentInputViewSizeSmall;
}
+
return self;
}
-- (void)setInputValue:(id)inputValue
-{
+- (void)setInputValue:(id)inputValue {
if ([inputValue respondsToSelector:@selector(stringValue)]) {
self.inputTextView.text = [inputValue stringValue];
}
}
-- (id)inputValue
-{
+- (id)inputValue {
return [FLEXRuntimeUtility valueForNumberWithObjCType:self.typeEncoding.UTF8String fromInputString:self.inputTextView.text];
}
-+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
-{
- static NSArray *primitiveTypes = nil;
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
+ NSParameterAssert(type);
+
+ static NSArray *supportedTypes = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
- primitiveTypes = @[@(@encode(char)),
- @(@encode(int)),
- @(@encode(short)),
- @(@encode(long)),
- @(@encode(long long)),
- @(@encode(unsigned char)),
- @(@encode(unsigned int)),
- @(@encode(unsigned short)),
- @(@encode(unsigned long)),
- @(@encode(unsigned long long)),
- @(@encode(float)),
- @(@encode(double)),
- @(@encode(long double))];
+ supportedTypes = @[
+ @FLEXEncodeClass(NSNumber),
+ @FLEXEncodeClass(NSDecimalNumber),
+ @(@encode(char)),
+ @(@encode(int)),
+ @(@encode(short)),
+ @(@encode(long)),
+ @(@encode(long long)),
+ @(@encode(unsigned char)),
+ @(@encode(unsigned int)),
+ @(@encode(unsigned short)),
+ @(@encode(unsigned long)),
+ @(@encode(unsigned long long)),
+ @(@encode(float)),
+ @(@encode(double)),
+ @(@encode(long double))
+ ];
});
- return type && [primitiveTypes containsObject:@(type)];
+
+ return type && [supportedTypes containsObject:@(type)];
}
@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.h
index 5f1e4d46..89cad157 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/15/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputTextView.h"
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.m
index 881d6c7b..7ed4c208 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputObjectView.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/15/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputObjectView.h"
@@ -25,8 +25,7 @@ @interface FLEXArgumentInputObjectView ()
@implementation FLEXArgumentInputObjectView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
// Start with the numbers and punctuation keyboard since quotes, curly braces, or
@@ -46,8 +45,7 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
return self;
}
-- (void)didChangeType
-{
+- (void)didChangeType {
self.inputType = self.objectTypeSegmentControl.selectedSegmentIndex;
if (super.inputValue) {
@@ -61,8 +59,7 @@ - (void)didChangeType
}
}
-- (void)setInputType:(FLEXArgInputObjectType)inputType
-{
+- (void)setInputType:(FLEXArgInputObjectType)inputType {
if (_inputType == inputType) return;
_inputType = inputType;
@@ -98,14 +95,12 @@ - (void)setInputType:(FLEXArgInputObjectType)inputType
[self.superview setNeedsLayout];
}
-- (void)setInputValue:(id)inputValue
-{
+- (void)setInputValue:(id)inputValue {
super.inputValue = inputValue;
[self populateTextAreaFromValue:inputValue];
}
-- (id)inputValue
-{
+- (id)inputValue {
switch (self.inputType) {
case FLEXArgInputObjectTypeJSON:
return [FLEXRuntimeUtility objectValueFromEditableJSONString:self.inputTextView.text];
@@ -122,8 +117,7 @@ - (id)inputValue
}
}
-- (void)populateTextAreaFromValue:(id)value
-{
+- (void)populateTextAreaFromValue:(id)value {
if (!value) {
self.inputTextView.text = nil;
} else {
@@ -138,16 +132,14 @@ - (void)populateTextAreaFromValue:(id)value
[self textViewDidChange:self.inputTextView];
}
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGSize fitSize = [super sizeThatFits:size];
fitSize.height += [self.objectTypeSegmentControl sizeThatFits:size].height + kSegmentInputMargin;
return fitSize;
}
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
CGFloat segmentHeight = [self.objectTypeSegmentControl sizeThatFits:self.frame.size].height;
self.objectTypeSegmentControl.frame = CGRectMake(
0.0,
@@ -162,23 +154,20 @@ - (void)layoutSubviews
[super layoutSubviews];
}
-- (CGFloat)topInputFieldVerticalLayoutGuide
-{
+- (CGFloat)topInputFieldVerticalLayoutGuide {
// Our text view is offset from the segmented control
CGFloat segmentHeight = [self.objectTypeSegmentControl sizeThatFits:self.frame.size].height;
return segmentHeight + super.topInputFieldVerticalLayoutGuide + kSegmentInputMargin;
}
-+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
-{
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
NSParameterAssert(type);
// Must be object type
- return type[0] == '@';
+ return type[0] == FLEXTypeEncodingObjcObject || type[0] == FLEXTypeEncodingObjcClass;
}
-+ (FLEXArgInputObjectType)preferredDefaultTypeForObjCType:(const char *)type withCurrentValue:(id)value
-{
- NSParameterAssert(type[0] == '@');
++ (FLEXArgInputObjectType)preferredDefaultTypeForObjCType:(const char *)type withCurrentValue:(id)value {
+ NSParameterAssert(type[0] == FLEXTypeEncodingObjcObject || type[0] == FLEXTypeEncodingObjcClass);
if (value) {
// If there's a current value, it must be serializable to JSON
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.h
index 2c16b6cb..484737ef 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/28/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputTextView.h"
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.m
index 3816e9e0..27ffffa5 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStringView.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/28/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputStringView.h"
@@ -11,36 +11,119 @@
@implementation FLEXArgumentInputStringView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
- self.targetSize = FLEXArgumentInputViewSizeLarge;
+ FLEXTypeEncoding type = typeEncoding[0];
+ if (type == FLEXTypeEncodingConst) {
+ // A crash here would mean an invalid type encoding string
+ type = typeEncoding[1];
+ }
+
+ // Selectors don't need a multi-line text box
+ if (type == FLEXTypeEncodingSelector) {
+ self.targetSize = FLEXArgumentInputViewSizeSmall;
+ } else {
+ self.targetSize = FLEXArgumentInputViewSizeLarge;
+ }
}
return self;
}
-- (void)setInputValue:(id)inputValue
-{
- self.inputTextView.text = inputValue;
+- (void)setInputValue:(id)inputValue {
+ if ([inputValue isKindOfClass:[NSString class]]) {
+ self.inputTextView.text = inputValue;
+ } else if ([inputValue isKindOfClass:[NSValue class]]) {
+ NSValue *value = (id)inputValue;
+ NSParameterAssert(strlen(value.objCType) == 1);
+
+ // C-String or SEL from NSValue
+ FLEXTypeEncoding type = value.objCType[0];
+ if (type == FLEXTypeEncodingConst) {
+ // A crash here would mean an invalid type encoding string
+ type = value.objCType[1];
+ }
+
+ if (type == FLEXTypeEncodingCString) {
+ self.inputTextView.text = @((const char *)value.pointerValue);
+ } else if (type == FLEXTypeEncodingSelector) {
+ self.inputTextView.text = NSStringFromSelector((SEL)value.pointerValue);
+ }
+ }
}
-- (id)inputValue
-{
+- (id)inputValue {
+ NSString *text = self.inputTextView.text;
// Interpret empty string as nil. We loose the ability to set empty string as a string value,
// but we accept that tradeoff in exchange for not having to type quotes for every string.
- return self.inputTextView.text.length > 0 ? [self.inputTextView.text copy] : nil;
+ if (!text.length) {
+ return nil;
+ }
+
+ // Case: C-strings and SELs
+ if (self.typeEncoding.length <= 2) {
+ FLEXTypeEncoding type = [self.typeEncoding characterAtIndex:0];
+ if (type == FLEXTypeEncodingConst) {
+ // A crash here would mean an invalid type encoding string
+ type = [self.typeEncoding characterAtIndex:1];
+ }
+
+ if (type == FLEXTypeEncodingCString || type == FLEXTypeEncodingSelector) {
+ const char *encoding = self.typeEncoding.UTF8String;
+ SEL selector = NSSelectorFromString(text);
+ return [NSValue valueWithBytes:&selector objCType:encoding];
+ }
+ }
+
+ // Case: NSStrings
+ return self.inputTextView.text.copy;
}
// TODO: Support using object address for strings, as in the object arg view.
-#pragma mark -
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
+ NSParameterAssert(type);
+ unsigned long len = strlen(type);
+
+ BOOL isConst = type[0] == FLEXTypeEncodingConst;
+ NSInteger i = isConst ? 1 : 0;
+
+ BOOL typeIsString = strcmp(type, FLEXEncodeClass(NSString)) == 0;
+ BOOL typeIsCString = len <= 2 && type[i] == FLEXTypeEncodingCString;
+ BOOL typeIsSEL = len <= 2 && type[i] == FLEXTypeEncodingSelector;
+ BOOL valueIsString = [value isKindOfClass:[NSString class]];
+
+ BOOL typeIsPrimitiveString = typeIsSEL || typeIsCString;
+ BOOL typeIsSupported = typeIsString || typeIsCString || typeIsSEL;
+
+ BOOL valueIsNSValueWithCorrectType = NO;
+ if ([value isKindOfClass:[NSValue class]]) {
+ NSValue *v = (id)value;
+ len = strlen(v.objCType);
+ if (len == 1) {
+ FLEXTypeEncoding type = v.objCType[i];
+ if (type == FLEXTypeEncodingCString && typeIsCString) {
+ valueIsNSValueWithCorrectType = YES;
+ } else if (type == FLEXTypeEncodingSelector && typeIsSEL) {
+ valueIsNSValueWithCorrectType = YES;
+ }
+ }
+ }
+
+ if (!value && typeIsSupported) {
+ return YES;
+ }
+
+ if (typeIsString && valueIsString) {
+ return YES;
+ }
+
+ // Primitive strings can be input as NSStrings or NSValues
+ if (typeIsPrimitiveString && (valueIsString || valueIsNSValueWithCorrectType)) {
+ return YES;
+ }
-+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
-{
- BOOL supported = type && strcmp(type, FLEXEncodeClass(NSString)) == 0;
- supported = supported || (value && [value isKindOfClass:[NSString class]]);
- return supported;
+ return NO;
}
@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.h
index 3e3b6f94..1a8b0349 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/16/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputView.h"
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.m
index f41e991c..eeb625ee 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputStructView.m
@@ -3,12 +3,13 @@
// Flipboard
//
// Created by Ryan Olson on 6/16/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputStructView.h"
#import "FLEXArgumentInputViewFactory.h"
#import "FLEXRuntimeUtility.h"
+#import "FLEXTypeEncodingParser.h"
@interface FLEXArgumentInputStructView ()
@@ -18,22 +19,26 @@ @interface FLEXArgumentInputStructView ()
@implementation FLEXArgumentInputStructView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
- NSMutableArray *inputViews = [NSMutableArray array];
+ NSMutableArray *inputViews = [NSMutableArray new];
NSArray *customTitles = [[self class] customFieldTitlesForTypeEncoding:typeEncoding];
- [FLEXRuntimeUtility enumerateTypesInStructEncoding:typeEncoding usingBlock:^(NSString *structName, const char *fieldTypeEncoding, NSString *prettyTypeEncoding, NSUInteger fieldIndex, NSUInteger fieldOffset) {
+ [FLEXRuntimeUtility enumerateTypesInStructEncoding:typeEncoding usingBlock:^(NSString *structName,
+ const char *fieldTypeEncoding,
+ NSString *prettyTypeEncoding,
+ NSUInteger fieldIndex,
+ NSUInteger fieldOffset) {
FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:fieldTypeEncoding];
- inputView.backgroundColor = self.backgroundColor;
inputView.targetSize = FLEXArgumentInputViewSizeSmall;
if (fieldIndex < customTitles.count) {
inputView.title = customTitles[fieldIndex];
} else {
- inputView.title = [NSString stringWithFormat:@"%@ field %lu (%@)", structName, (unsigned long)fieldIndex, prettyTypeEncoding];
+ inputView.title = [NSString stringWithFormat:@"%@ field %lu (%@)",
+ structName, (unsigned long)fieldIndex, prettyTypeEncoding
+ ];
}
[inputViews addObject:inputView];
@@ -47,29 +52,27 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
#pragma mark - Superclass Overrides
-- (void)setBackgroundColor:(UIColor *)backgroundColor
-{
+- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
for (FLEXArgumentInputView *inputView in self.argumentInputViews) {
inputView.backgroundColor = backgroundColor;
}
}
-- (void)setInputValue:(id)inputValue
-{
+- (void)setInputValue:(id)inputValue {
if ([inputValue isKindOfClass:[NSValue class]]) {
const char *structTypeEncoding = [inputValue objCType];
if (strcmp(self.typeEncoding.UTF8String, structTypeEncoding) == 0) {
NSUInteger valueSize = 0;
- @try {
- // NSGetSizeAndAlignment barfs on type encoding for bitfields.
- NSGetSizeAndAlignment(structTypeEncoding, &valueSize, NULL);
- } @catch (NSException *exception) { }
- if (valueSize > 0) {
+ if (FLEXGetSizeAndAlignment(structTypeEncoding, &valueSize, NULL)) {
void *unboxedValue = malloc(valueSize);
[inputValue getValue:unboxedValue];
- [FLEXRuntimeUtility enumerateTypesInStructEncoding:structTypeEncoding usingBlock:^(NSString *structName, const char *fieldTypeEncoding, NSString *prettyTypeEncoding, NSUInteger fieldIndex, NSUInteger fieldOffset) {
+ [FLEXRuntimeUtility enumerateTypesInStructEncoding:structTypeEncoding usingBlock:^(NSString *structName,
+ const char *fieldTypeEncoding,
+ NSString *prettyTypeEncoding,
+ NSUInteger fieldIndex,
+ NSUInteger fieldOffset) {
void *fieldPointer = unboxedValue + fieldOffset;
FLEXArgumentInputView *inputView = self.argumentInputViews[fieldIndex];
@@ -87,19 +90,18 @@ - (void)setInputValue:(id)inputValue
}
}
-- (id)inputValue
-{
+- (id)inputValue {
NSValue *boxedStruct = nil;
const char *structTypeEncoding = self.typeEncoding.UTF8String;
NSUInteger structSize = 0;
- @try {
- // NSGetSizeAndAlignment barfs on type encoding for bitfields.
- NSGetSizeAndAlignment(structTypeEncoding, &structSize, NULL);
- } @catch (NSException *exception) { }
- if (structSize > 0) {
+ if (FLEXGetSizeAndAlignment(structTypeEncoding, &structSize, NULL)) {
void *unboxedStruct = malloc(structSize);
- [FLEXRuntimeUtility enumerateTypesInStructEncoding:structTypeEncoding usingBlock:^(NSString *structName, const char *fieldTypeEncoding, NSString *prettyTypeEncoding, NSUInteger fieldIndex, NSUInteger fieldOffset) {
+ [FLEXRuntimeUtility enumerateTypesInStructEncoding:structTypeEncoding usingBlock:^(NSString *structName,
+ const char *fieldTypeEncoding,
+ NSString *prettyTypeEncoding,
+ NSUInteger fieldIndex,
+ NSUInteger fieldOffset) {
void *fieldPointer = unboxedStruct + fieldOffset;
FLEXArgumentInputView *inputView = self.argumentInputViews[fieldIndex];
@@ -123,8 +125,7 @@ - (id)inputValue
return boxedStruct;
}
-- (BOOL)inputViewIsFirstResponder
-{
+- (BOOL)inputViewIsFirstResponder {
BOOL isFirstResponder = NO;
for (FLEXArgumentInputView *inputView in self.argumentInputViews) {
if ([inputView inputViewIsFirstResponder]) {
@@ -138,8 +139,7 @@ - (BOOL)inputViewIsFirstResponder
#pragma mark - Layout and Sizing
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
[super layoutSubviews];
CGFloat runningOriginY = self.topInputFieldVerticalLayoutGuide;
@@ -151,13 +151,11 @@ - (void)layoutSubviews
}
}
-+ (CGFloat)verticalPaddingBetweenFields
-{
++ (CGFloat)verticalPaddingBetweenFields {
return 10.0;
}
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGSize fitSize = [super sizeThatFits:size];
CGSize constrainSize = CGSizeMake(size.width, CGFLOAT_MAX);
@@ -174,13 +172,16 @@ - (CGSize)sizeThatFits:(CGSize)size
#pragma mark - Class Helpers
-+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
-{
- return type && type[0] == FLEXTypeEncodingStructBegin;
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
+ NSParameterAssert(type);
+ if (type[0] == FLEXTypeEncodingStructBegin) {
+ return FLEXGetSizeAndAlignment(type, nil, nil);
+ }
+
+ return NO;
}
-+ (NSArray *)customFieldTitlesForTypeEncoding:(const char *)typeEncoding
-{
++ (NSArray *)customFieldTitlesForTypeEncoding:(const char *)typeEncoding {
NSArray *customTitles = nil;
if (strcmp(typeEncoding, @encode(CGRect)) == 0) {
customTitles = @[@"CGPoint origin", @"CGSize size"];
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.h
index 973639df..a1ae58ab 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/16/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputView.h"
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.m
index fc12b908..bd0ddf79 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputSwitchView.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 6/16/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputSwitchView.h"
@@ -16,8 +16,7 @@ @interface FLEXArgumentInputSwitchView ()
@implementation FLEXArgumentInputSwitchView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
self.inputSwitch = [UISwitch new];
@@ -31,8 +30,7 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
#pragma mark Input/Output
-- (void)setInputValue:(id)inputValue
-{
+- (void)setInputValue:(id)inputValue {
BOOL on = NO;
if ([inputValue isKindOfClass:[NSNumber class]]) {
NSNumber *number = (NSNumber *)inputValue;
@@ -46,30 +44,26 @@ - (void)setInputValue:(id)inputValue
self.inputSwitch.on = on;
}
-- (id)inputValue
-{
+- (id)inputValue {
BOOL isOn = [self.inputSwitch isOn];
NSValue *boxedBool = [NSValue value:&isOn withObjCType:@encode(BOOL)];
return boxedBool;
}
-- (void)switchValueDidChange:(id)sender
-{
+- (void)switchValueDidChange:(id)sender {
[self.delegate argumentInputViewValueDidChange:self];
}
#pragma mark - Layout and Sizing
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
[super layoutSubviews];
self.inputSwitch.frame = CGRectMake(0, self.topInputFieldVerticalLayoutGuide, self.inputSwitch.frame.size.width, self.inputSwitch.frame.size.height);
}
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGSize fitSize = [super sizeThatFits:size];
fitSize.height += self.inputSwitch.frame.size.height;
return fitSize;
@@ -78,10 +72,10 @@ - (CGSize)sizeThatFits:(CGSize)size
#pragma mark - Class Helpers
-+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
-{
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
+ NSParameterAssert(type);
// Only BOOLs. Current value is irrelevant.
- return type && strcmp(type, @encode(BOOL)) == 0;
+ return strcmp(type, @encode(BOOL)) == 0;
}
@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputTextView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputTextView.m
index da7ddca6..30d92b5a 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputTextView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputTextView.m
@@ -20,26 +20,31 @@ @interface FLEXArgumentInputTextView ()
@implementation FLEXArgumentInputTextView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithArgumentTypeEncoding:typeEncoding];
if (self) {
self.inputTextView = [UITextView new];
self.inputTextView.font = [[self class] inputFont];
- self.inputTextView.backgroundColor = [FLEXColor primaryBackgroundColor];
- self.inputTextView.layer.borderColor = [FLEXColor borderColor].CGColor;
- self.inputTextView.layer.borderWidth = 1.f;
- self.inputTextView.layer.cornerRadius = 5.f;
+ self.inputTextView.backgroundColor = FLEXColor.secondaryGroupedBackgroundColor;
+ self.inputTextView.layer.cornerRadius = 10.f;
+ self.inputTextView.contentInset = UIEdgeInsetsMake(0, 5, 0, 0);
self.inputTextView.autocapitalizationType = UITextAutocapitalizationTypeNone;
self.inputTextView.autocorrectionType = UITextAutocorrectionTypeNo;
self.inputTextView.delegate = self;
self.inputTextView.inputAccessoryView = [self createToolBar];
- [self addSubview:self.inputTextView];
+ if (@available(iOS 11, *)) {
+ [self.inputTextView.layer setValue:@YES forKey:@"continuousCorners"];
+ } else {
+ self.inputTextView.layer.borderWidth = 1.f;
+ self.inputTextView.layer.borderColor = FLEXColor.borderColor.CGColor;
+ }
self.placeholderLabel = [UILabel new];
self.placeholderLabel.font = self.inputTextView.font;
- self.placeholderLabel.textColor = [FLEXColor deemphasizedTextColor];
+ self.placeholderLabel.textColor = FLEXColor.deemphasizedTextColor;
self.placeholderLabel.numberOfLines = 0;
+
+ [self addSubview:self.inputTextView];
[self.inputTextView addSubview:self.placeholderLabel];
}
@@ -48,23 +53,26 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
#pragma mark - Private
-- (UIToolbar *)createToolBar
-{
+- (UIToolbar *)createToolBar {
UIToolbar *toolBar = [UIToolbar new];
[toolBar sizeToFit];
- UIBarButtonItem *spaceItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
- UIBarButtonItem *doneItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(textViewDone)];
- toolBar.items = @[spaceItem, doneItem];
+ UIBarButtonItem *spaceItem = [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
+ target:nil action:nil
+ ];
+ UIBarButtonItem *pasteItem = [[UIBarButtonItem alloc]
+ initWithTitle:@"Paste" style:UIBarButtonItemStyleDone
+ target:self.inputTextView action:@selector(paste:)
+ ];
+ UIBarButtonItem *doneItem = [[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+ target:self.inputTextView action:@selector(resignFirstResponder)
+ ];
+ toolBar.items = @[spaceItem, pasteItem, doneItem];
return toolBar;
}
-- (void)textViewDone
-{
- [self.inputTextView resignFirstResponder];
-}
-
-- (void)setInputPlaceholderText:(NSString *)placeholder
-{
+- (void)setInputPlaceholderText:(NSString *)placeholder {
self.placeholderLabel.text = placeholder;
if (placeholder.length) {
if (!self.inputTextView.text.length) {
@@ -79,41 +87,35 @@ - (void)setInputPlaceholderText:(NSString *)placeholder
[self setNeedsLayout];
}
-- (NSString *)inputPlaceholderText
-{
+- (NSString *)inputPlaceholderText {
return self.placeholderLabel.text;
}
#pragma mark - Superclass Overrides
-- (BOOL)inputViewIsFirstResponder
-{
+- (BOOL)inputViewIsFirstResponder {
return self.inputTextView.isFirstResponder;
}
#pragma mark - Layout and Sizing
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
[super layoutSubviews];
self.inputTextView.frame = CGRectMake(0, self.topInputFieldVerticalLayoutGuide, self.bounds.size.width, [self inputTextViewHeight]);
- // Placeholder label is positioned by insetting origin,
- // which is the line fragment padding for X and 0 for Y,
+ // Placeholder label is positioned by insetting then origin
// by the content inset then the text container inset
- CGFloat leading = self.inputTextView.textContainer.lineFragmentPadding;
CGSize s = self.inputTextView.frame.size;
- self.placeholderLabel.frame = CGRectMake(leading, 0, s.width, s.height);
+ self.placeholderLabel.frame = CGRectMake(0, 0, s.width, s.height);
self.placeholderLabel.frame = UIEdgeInsetsInsetRect(
UIEdgeInsetsInsetRect(self.placeholderLabel.frame, self.inputTextView.contentInset),
self.inputTextView.textContainerInset
);
}
-- (NSUInteger)numberOfInputLines
-{
+- (NSUInteger)numberOfInputLines {
switch (self.targetSize) {
case FLEXArgumentInputViewSizeDefault:
return 2;
@@ -124,45 +126,27 @@ - (NSUInteger)numberOfInputLines
}
}
-- (CGFloat)inputTextViewHeight
-{
+- (CGFloat)inputTextViewHeight {
return ceil([[self class] inputFont].lineHeight * self.numberOfInputLines) + 16.0;
}
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGSize fitSize = [super sizeThatFits:size];
fitSize.height += [self inputTextViewHeight];
return fitSize;
}
-#pragma mark - Trait collection changes
-
-- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
-{
-#if FLEX_AT_LEAST_IOS13_SDK
- if (@available(iOS 13.0, *)) {
- if (previousTraitCollection.userInterfaceStyle != self.traitCollection.userInterfaceStyle) {
- self.inputTextView.layer.borderColor = [FLEXColor borderColor].CGColor;
- }
- }
-#endif
-}
-
-
#pragma mark - Class Helpers
-+ (UIFont *)inputFont
-{
- return [FLEXUtility defaultFontOfSize:14.0];
++ (UIFont *)inputFont {
+ return [UIFont systemFontOfSize:14.0];
}
#pragma mark - UITextViewDelegate
-- (void)textViewDidChange:(UITextView *)textView
-{
+- (void)textViewDidChange:(UITextView *)textView {
[self.delegate argumentInputViewValueDidChange:self];
self.placeholderLabel.hidden = !(self.inputPlaceholderText.length && !textView.text.length);
}
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.h b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.h
index 677e509d..d09a1395 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.h
@@ -3,14 +3,17 @@
// Flipboard
//
// Created by Ryan Olson on 5/30/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import
typedef NS_ENUM(NSUInteger, FLEXArgumentInputViewSize) {
+ /// 2 lines, medium-sized
FLEXArgumentInputViewSizeDefault = 0,
+ /// One line
FLEXArgumentInputViewSizeSmall,
+ /// Several lines
FLEXArgumentInputViewSizeLarge
};
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.m b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.m
index be3b1a50..513bd6a3 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputView.m
@@ -3,11 +3,12 @@
// Flipboard
//
// Created by Ryan Olson on 5/30/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXArgumentInputView.h"
#import "FLEXUtility.h"
+#import "FLEXColor.h"
@interface FLEXArgumentInputView ()
@@ -18,8 +19,7 @@ @interface FLEXArgumentInputView ()
@implementation FLEXArgumentInputView
-- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
-{
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding {
self = [super initWithFrame:CGRectZero];
if (self) {
self.typeEncoding = typeEncoding != NULL ? @(typeEncoding) : nil;
@@ -27,8 +27,7 @@ - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
return self;
}
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
[super layoutSubviews];
if (self.showsTitle) {
@@ -38,14 +37,12 @@ - (void)layoutSubviews
}
}
-- (void)setBackgroundColor:(UIColor *)backgroundColor
-{
+- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
self.titleLabel.backgroundColor = backgroundColor;
}
-- (void)setTitle:(NSString *)title
-{
+- (void)setTitle:(NSString *)title {
if (![_title isEqual:title]) {
_title = title;
self.titleLabel.text = title;
@@ -53,26 +50,22 @@ - (void)setTitle:(NSString *)title
}
}
-- (UILabel *)titleLabel
-{
+- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [UILabel new];
_titleLabel.font = [[self class] titleFont];
- _titleLabel.backgroundColor = self.backgroundColor;
- _titleLabel.textColor = [UIColor colorWithWhite:0.3 alpha:1.0];
+ _titleLabel.textColor = FLEXColor.primaryTextColor;
_titleLabel.numberOfLines = 0;
[self addSubview:_titleLabel];
}
return _titleLabel;
}
-- (BOOL)showsTitle
-{
+- (BOOL)showsTitle {
return self.title.length > 0;
}
-- (CGFloat)topInputFieldVerticalLayoutGuide
-{
+- (CGFloat)topInputFieldVerticalLayoutGuide {
CGFloat verticalLayoutGuide = 0;
if (self.showsTitle) {
CGFloat titleHeight = [self.titleLabel sizeThatFits:self.bounds.size].height;
@@ -84,34 +77,29 @@ - (CGFloat)topInputFieldVerticalLayoutGuide
#pragma mark - Subclasses Can Override
-- (BOOL)inputViewIsFirstResponder
-{
+- (BOOL)inputViewIsFirstResponder {
return NO;
}
-+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
-{
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value {
return NO;
}
#pragma mark - Class Helpers
-+ (UIFont *)titleFont
-{
- return [FLEXUtility defaultFontOfSize:12.0];
++ (UIFont *)titleFont {
+ return [UIFont systemFontOfSize:12.0];
}
-+ (CGFloat)titleBottomPadding
-{
++ (CGFloat)titleBottomPadding {
return 4.0;
}
#pragma mark - Sizing
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGFloat height = 0;
if (self.title.length > 0) {
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputViewFactory.h b/xcode/Pods/FLEX/Classes/Editing/FLEXArgumentInputViewFactory.h
similarity index 95%
rename from xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputViewFactory.h
rename to xcode/Pods/FLEX/Classes/Editing/FLEXArgumentInputViewFactory.h
index 0680903e..f72e9073 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputViewFactory.h
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXArgumentInputViewFactory.h
@@ -7,8 +7,7 @@
//
#import
-
-@class FLEXArgumentInputView;
+#import "FLEXArgumentInputSwitchView.h"
@interface FLEXArgumentInputViewFactory : NSObject
diff --git a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputViewFactory.m b/xcode/Pods/FLEX/Classes/Editing/FLEXArgumentInputViewFactory.m
similarity index 95%
rename from xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputViewFactory.m
rename to xcode/Pods/FLEX/Classes/Editing/FLEXArgumentInputViewFactory.m
index 93a46aa7..fde1c8f3 100644
--- a/xcode/Pods/FLEX/Classes/Editing/ArgumentInputViews/FLEXArgumentInputViewFactory.m
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXArgumentInputViewFactory.m
@@ -21,13 +21,11 @@
@implementation FLEXArgumentInputViewFactory
-+ (FLEXArgumentInputView *)argumentInputViewForTypeEncoding:(const char *)typeEncoding
-{
++ (FLEXArgumentInputView *)argumentInputViewForTypeEncoding:(const char *)typeEncoding {
return [self argumentInputViewForTypeEncoding:typeEncoding currentValue:nil];
}
-+ (FLEXArgumentInputView *)argumentInputViewForTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue
-{
++ (FLEXArgumentInputView *)argumentInputViewForTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue {
Class subclass = [self argumentInputViewSubclassForTypeEncoding:typeEncoding currentValue:currentValue];
if (!subclass) {
// Fall back to a FLEXArgumentInputNotSupportedView if we can't find a subclass that fits the type encoding.
@@ -39,8 +37,7 @@ + (FLEXArgumentInputView *)argumentInputViewForTypeEncoding:(const char *)typeEn
return [[subclass alloc] initWithArgumentTypeEncoding:typeEncoding + fieldNameOffset];
}
-+ (Class)argumentInputViewSubclassForTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue
-{
++ (Class)argumentInputViewSubclassForTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue {
// Remove the field name if there is any (e.g. \"width\"d -> d)
const NSUInteger fieldNameOffset = [FLEXRuntimeUtility fieldNameOffsetForTypeEncoding:typeEncoding];
Class argumentInputViewSubclass = nil;
@@ -66,8 +63,7 @@ + (Class)argumentInputViewSubclassForTypeEncoding:(const char *)typeEncoding cur
return argumentInputViewSubclass;
}
-+ (BOOL)canEditFieldWithTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue
-{
++ (BOOL)canEditFieldWithTypeEncoding:(const char *)typeEncoding currentValue:(id)currentValue {
return [self argumentInputViewSubclassForTypeEncoding:typeEncoding currentValue:currentValue] != nil;
}
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXDefaultEditorViewController.h b/xcode/Pods/FLEX/Classes/Editing/FLEXDefaultEditorViewController.h
index 98df087f..1f36193b 100644
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXDefaultEditorViewController.h
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXDefaultEditorViewController.h
@@ -3,12 +3,12 @@
// Flipboard
//
// Created by Ryan Olson on 5/23/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
-#import "FLEXMutableFieldEditorViewController.h"
+#import "FLEXFieldEditorViewController.h"
-@interface FLEXDefaultEditorViewController : FLEXMutableFieldEditorViewController
+@interface FLEXDefaultEditorViewController : FLEXFieldEditorViewController
- (id)initWithDefaults:(NSUserDefaults *)defaults key:(NSString *)key;
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXDefaultEditorViewController.m b/xcode/Pods/FLEX/Classes/Editing/FLEXDefaultEditorViewController.m
index 2e2deca6..739aa988 100644
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXDefaultEditorViewController.m
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXDefaultEditorViewController.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 5/23/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXDefaultEditorViewController.h"
@@ -21,8 +21,7 @@ @interface FLEXDefaultEditorViewController ()
@implementation FLEXDefaultEditorViewController
-- (id)initWithDefaults:(NSUserDefaults *)defaults key:(NSString *)key
-{
+- (id)initWithDefaults:(NSUserDefaults *)defaults key:(NSString *)key {
self = [super initWithTarget:defaults];
if (self) {
self.key = key;
@@ -31,13 +30,11 @@ - (id)initWithDefaults:(NSUserDefaults *)defaults key:(NSString *)key
return self;
}
-- (NSUserDefaults *)defaults
-{
+- (NSUserDefaults *)defaults {
return [self.target isKindOfClass:[NSUserDefaults class]] ? self.target : nil;
}
-- (void)viewDidLoad
-{
+- (void)viewDidLoad {
[super viewDidLoad];
self.fieldEditorView.fieldDescription = self.key;
@@ -52,8 +49,7 @@ - (void)viewDidLoad
self.fieldEditorView.argumentInputViews = @[inputView];
}
-- (void)actionButtonPressed:(id)sender
-{
+- (void)actionButtonPressed:(id)sender {
[super actionButtonPressed:sender];
id value = self.firstInputView.inputValue;
@@ -67,15 +63,13 @@ - (void)actionButtonPressed:(id)sender
self.firstInputView.inputValue = [self.defaults objectForKey:self.key];
}
-- (void)getterButtonPressed:(id)sender
-{
+- (void)getterButtonPressed:(id)sender {
[super getterButtonPressed:sender];
id returnedObject = [self.defaults objectForKey:self.key];
[self exploreObjectOrPopViewController:returnedObject];
}
-+ (BOOL)canEditDefaultWithValue:(id)currentValue
-{
++ (BOOL)canEditDefaultWithValue:(id)currentValue {
return [FLEXArgumentInputViewFactory
canEditFieldWithTypeEncoding:FLEXEncodeObject(currentValue)
currentValue:currentValue
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorView.h b/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorView.h
index 780d5641..1345805a 100644
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorView.h
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorView.h
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 5/16/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import
@@ -15,6 +15,6 @@
@property (nonatomic, copy) NSString *targetDescription;
@property (nonatomic, copy) NSString *fieldDescription;
-@property (nonatomic) NSArray *argumentInputViews;
+@property (nonatomic, copy) NSArray *argumentInputViews;
@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorView.m b/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorView.m
index c5898cd5..5295261e 100644
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorView.m
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorView.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 5/16/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXFieldEditorView.h"
@@ -21,8 +21,7 @@ @interface FLEXFieldEditorView ()
@implementation FLEXFieldEditorView
-- (id)initWithFrame:(CGRect)frame
-{
+- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.targetDescriptionLabel = [UILabel new];
@@ -44,8 +43,7 @@ - (id)initWithFrame:(CGRect)frame
return self;
}
-- (void)layoutSubviews
-{
+- (void)layoutSubviews {
[super layoutSubviews];
CGFloat horizontalPadding = [[self class] horizontalPadding];
@@ -78,15 +76,13 @@ - (void)layoutSubviews
}
}
-- (void)setBackgroundColor:(UIColor *)backgroundColor
-{
+- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
self.targetDescriptionLabel.backgroundColor = backgroundColor;
self.fieldDescriptionLabel.backgroundColor = backgroundColor;
}
-- (void)setTargetDescription:(NSString *)targetDescription
-{
+- (void)setTargetDescription:(NSString *)targetDescription {
if (![_targetDescription isEqual:targetDescription]) {
_targetDescription = targetDescription;
self.targetDescriptionLabel.text = targetDescription;
@@ -94,8 +90,7 @@ - (void)setTargetDescription:(NSString *)targetDescription
}
}
-- (void)setFieldDescription:(NSString *)fieldDescription
-{
+- (void)setFieldDescription:(NSString *)fieldDescription {
if (![_fieldDescription isEqual:fieldDescription]) {
_fieldDescription = fieldDescription;
self.fieldDescriptionLabel.text = fieldDescription;
@@ -103,8 +98,7 @@ - (void)setFieldDescription:(NSString *)fieldDescription
}
}
-- (void)setArgumentInputViews:(NSArray *)argumentInputViews
-{
+- (void)setArgumentInputViews:(NSArray *)argumentInputViews {
if (![_argumentInputViews isEqual:argumentInputViews]) {
for (FLEXArgumentInputView *inputView in _argumentInputViews) {
@@ -121,40 +115,33 @@ - (void)setArgumentInputViews:(NSArray *)argumentInputV
}
}
-+ (UIView *)dividerView
-{
++ (UIView *)dividerView {
UIView *dividerView = [UIView new];
dividerView.backgroundColor = [self dividerColor];
return dividerView;
}
-+ (UIColor *)dividerColor
-{
++ (UIColor *)dividerColor {
return UIColor.lightGrayColor;
}
-+ (CGFloat)horizontalPadding
-{
++ (CGFloat)horizontalPadding {
return 10.0;
}
-+ (CGFloat)verticalPadding
-{
++ (CGFloat)verticalPadding {
return 20.0;
}
-+ (UIFont *)labelFont
-{
- return [FLEXUtility defaultFontOfSize:14.0];
++ (UIFont *)labelFont {
+ return [UIFont systemFontOfSize:14.0];
}
-+ (CGFloat)dividerLineHeight
-{
++ (CGFloat)dividerLineHeight {
return 1.0;
}
-- (CGSize)sizeThatFits:(CGSize)size
-{
+- (CGSize)sizeThatFits:(CGSize)size {
CGFloat horizontalPadding = [[self class] horizontalPadding];
CGFloat verticalPadding = [[self class] verticalPadding];
CGFloat dividerLineHeight = [[self class] dividerLineHeight];
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorViewController.h b/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorViewController.h
index 1b6afef8..e109cebd 100644
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorViewController.h
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorViewController.h
@@ -1,32 +1,29 @@
//
// FLEXFieldEditorViewController.h
-// Flipboard
+// FLEX
//
-// Created by Ryan Olson on 5/16/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Created by Tanner on 11/22/18.
+// Copyright © 2018 Flipboard. All rights reserved.
//
-#import
+#import "FLEXVariableEditorViewController.h"
+#import "FLEXProperty.h"
+#import "FLEXIvar.h"
-@class FLEXFieldEditorView;
-@class FLEXArgumentInputView;
+NS_ASSUME_NONNULL_BEGIN
-@interface FLEXFieldEditorViewController : UIViewController
+@interface FLEXFieldEditorViewController : FLEXVariableEditorViewController
-- (id)initWithTarget:(id)target;
+/// @return nil if the property is readonly or if the type is unsupported
++ (nullable instancetype)target:(id)target property:(FLEXProperty *)property;
+/// @return nil if the ivar type is unsupported
++ (nullable instancetype)target:(id)target ivar:(FLEXIvar *)ivar;
-// Convenience accessor since many subclasses only use one input view
-@property (nonatomic, readonly) FLEXArgumentInputView *firstInputView;
+/// Subclasses can change the button title via the \c title property
+@property (nonatomic, readonly) UIBarButtonItem *getterButton;
-// For subclass use only.
-@property (nonatomic, readonly) id target;
-@property (nonatomic, readonly) FLEXFieldEditorView *fieldEditorView;
-@property (nonatomic, readonly) UIBarButtonItem *setterButton;
-
-- (void)actionButtonPressed:(id)sender;
-- (NSString *)titleForActionButton;
-/// Pushes an explorer view controller for the given object
-/// or pops the current view controller.
-- (void)exploreObjectOrPopViewController:(id)objectOrNil;
+- (void)getterButtonPressed:(id)sender;
@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorViewController.m b/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorViewController.m
index 4d8d93b6..7a5db17d 100644
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorViewController.m
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXFieldEditorViewController.m
@@ -1,132 +1,163 @@
//
// FLEXFieldEditorViewController.m
-// Flipboard
+// FLEX
//
-// Created by Ryan Olson on 5/16/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Created by Tanner on 11/22/18.
+// Copyright © 2018 Flipboard. All rights reserved.
//
-#import "FLEXColor.h"
#import "FLEXFieldEditorViewController.h"
#import "FLEXFieldEditorView.h"
+#import "FLEXArgumentInputViewFactory.h"
+#import "FLEXPropertyAttributes.h"
#import "FLEXRuntimeUtility.h"
#import "FLEXUtility.h"
-#import "FLEXObjectExplorerFactory.h"
-#import "FLEXArgumentInputView.h"
-#import "FLEXArgumentInputViewFactory.h"
-#import "FLEXObjectExplorerViewController.h"
+#import "FLEXColor.h"
+#import "UIBarButtonItem+FLEX.h"
-@interface FLEXFieldEditorViewController ()
+@interface FLEXFieldEditorViewController ()
-@property (nonatomic) UIScrollView *scrollView;
+@property (nonatomic) FLEXProperty *property;
+@property (nonatomic) FLEXIvar *ivar;
-@property (nonatomic, readwrite) id target;
-@property (nonatomic, readwrite) FLEXFieldEditorView *fieldEditorView;
-@property (nonatomic, readwrite) UIBarButtonItem *setterButton;
+@property (nonatomic, readonly) id currentValue;
+@property (nonatomic, readonly) const FLEXTypeEncoding *typeEncoding;
+@property (nonatomic, readonly) NSString *fieldDescription;
@end
@implementation FLEXFieldEditorViewController
-- (id)initWithTarget:(id)target
-{
- self = [super initWithNibName:nil bundle:nil];
- if (self) {
- self.target = target;
- [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
- [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
+#pragma mark - Initialization
+
++ (instancetype)target:(id)target property:(FLEXProperty *)property {
+ id value = [property getValue:target];
+ if (![self canEditProperty:property onObject:target currentValue:value]) {
+ return nil;
}
- return self;
+
+ FLEXFieldEditorViewController *editor = [self target:target];
+ editor.title = [@"Property: " stringByAppendingString:property.name];
+ editor.property = property;
+ return editor;
}
-- (void)dealloc
-{
- [NSNotificationCenter.defaultCenter removeObserver:self];
++ (instancetype)target:(id)target ivar:(nonnull FLEXIvar *)ivar {
+ FLEXFieldEditorViewController *editor = [self target:target];
+ editor.title = [@"Ivar: " stringByAppendingString:ivar.name];
+ editor.ivar = ivar;
+ return editor;
}
-- (void)keyboardDidShow:(NSNotification *)notification
-{
- CGRect keyboardRectInWindow = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
- CGSize keyboardSize = [self.view convertRect:keyboardRectInWindow fromView:nil].size;
- UIEdgeInsets scrollInsets = self.scrollView.contentInset;
- scrollInsets.bottom = keyboardSize.height;
- self.scrollView.contentInset = scrollInsets;
- self.scrollView.scrollIndicatorInsets = scrollInsets;
-
- // Find the active input view and scroll to make sure it's visible.
- for (FLEXArgumentInputView *argumentInputView in self.fieldEditorView.argumentInputViews) {
- if (argumentInputView.inputViewIsFirstResponder) {
- CGRect scrollToVisibleRect = [self.scrollView convertRect:argumentInputView.bounds fromView:argumentInputView];
- [self.scrollView scrollRectToVisible:scrollToVisibleRect animated:YES];
- break;
- }
+#pragma mark - Overrides
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.view.backgroundColor = FLEXColor.groupedBackgroundColor;
+
+ // Create getter button
+ _getterButton = [[UIBarButtonItem alloc]
+ initWithTitle:@"Get"
+ style:UIBarButtonItemStyleDone
+ target:self
+ action:@selector(getterButtonPressed:)
+ ];
+ self.toolbarItems = @[
+ UIBarButtonItem.flex_flexibleSpace, self.getterButton, self.actionButton
+ ];
+
+ // Configure input view
+ self.fieldEditorView.fieldDescription = self.fieldDescription;
+ FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:self.typeEncoding];
+ inputView.inputValue = self.currentValue;
+ inputView.delegate = self;
+ self.fieldEditorView.argumentInputViews = @[inputView];
+
+ // Don't show a "set" button for switches; we mutate when the switch is flipped
+ if ([inputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
+ self.actionButton.enabled = NO;
+ self.actionButton.title = @"Flip the switch to call the setter";
+ // Put getter button before setter button
+ self.toolbarItems = @[
+ UIBarButtonItem.flex_flexibleSpace, self.actionButton, self.getterButton
+ ];
}
}
-- (void)keyboardWillHide:(NSNotification *)notification
-{
- UIEdgeInsets scrollInsets = self.scrollView.contentInset;
- scrollInsets.bottom = 0.0;
- self.scrollView.contentInset = scrollInsets;
- self.scrollView.scrollIndicatorInsets = scrollInsets;
-}
+- (void)actionButtonPressed:(id)sender {
+ [super actionButtonPressed:sender];
+
+ if (self.property) {
+ id userInputObject = self.firstInputView.inputValue;
+ NSArray *arguments = userInputObject ? @[userInputObject] : nil;
+ SEL setterSelector = self.property.likelySetter;
+ NSError *error = nil;
+ [FLEXRuntimeUtility performSelector:setterSelector onObject:self.target withArguments:arguments error:&error];
+ if (error) {
+ [FLEXAlert showAlert:@"Property Setter Failed" message:error.localizedDescription from:self];
+ sender = nil; // Don't pop back
+ }
+ } else {
+ // TODO: check mutability and use mutableCopy if necessary;
+ // this currently could and would assign NSArray to NSMutableArray
+ [self.ivar setValue:self.firstInputView.inputValue onObject:self.target];
+ }
-- (void)viewDidLoad
-{
- [super viewDidLoad];
-
- self.view.backgroundColor = [FLEXColor scrollViewBackgroundColor];
-
- self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
- self.scrollView.backgroundColor = self.view.backgroundColor;
- self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- self.scrollView.delegate = self;
- [self.view addSubview:self.scrollView];
-
- self.fieldEditorView = [FLEXFieldEditorView new];
- self.fieldEditorView.backgroundColor = self.view.backgroundColor;
- self.fieldEditorView.targetDescription = [NSString stringWithFormat:@"%@ %p", [self.target class], self.target];
- [self.scrollView addSubview:self.fieldEditorView];
-
- self.setterButton = [[UIBarButtonItem alloc] initWithTitle:[self titleForActionButton] style:UIBarButtonItemStyleDone target:self action:@selector(actionButtonPressed:)];
- self.navigationItem.rightBarButtonItem = self.setterButton;
+ // Go back after setting, but not for switches.
+ if (sender) {
+ [self.navigationController popViewControllerAnimated:YES];
+ } else {
+ self.firstInputView.inputValue = self.currentValue;
+ }
}
-- (void)viewWillLayoutSubviews
-{
- CGSize constrainSize = CGSizeMake(self.scrollView.bounds.size.width, CGFLOAT_MAX);
- CGSize fieldEditorSize = [self.fieldEditorView sizeThatFits:constrainSize];
- self.fieldEditorView.frame = CGRectMake(0, 0, fieldEditorSize.width, fieldEditorSize.height);
- self.scrollView.contentSize = fieldEditorSize;
+- (void)getterButtonPressed:(id)sender {
+ [self.fieldEditorView endEditing:YES];
+
+ [self exploreObjectOrPopViewController:self.currentValue];
}
-- (FLEXArgumentInputView *)firstInputView
-{
- return [self.fieldEditorView argumentInputViews].firstObject;
+- (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)argumentInputView {
+ if ([argumentInputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
+ [self actionButtonPressed:nil];
+ }
}
-- (void)actionButtonPressed:(id)sender
-{
- // Subclasses can override
- [self.fieldEditorView endEditing:YES];
+#pragma mark - Private
+
+- (id)currentValue {
+ if (self.property) {
+ return [self.property getValue:self.target];
+ } else {
+ return [self.ivar getValue:self.target];
+ }
}
-- (NSString *)titleForActionButton
-{
- // Subclasses can override.
- return @"Set";
+- (const FLEXTypeEncoding *)typeEncoding {
+ if (self.property) {
+ return self.property.attributes.typeEncoding.UTF8String;
+ } else {
+ return self.ivar.typeEncoding.UTF8String;
+ }
}
-- (void)exploreObjectOrPopViewController:(id)objectOrNil {
- if (objectOrNil) {
- // For non-nil (or void) return types, push an explorer view controller to display the object
- FLEXObjectExplorerViewController *explorerViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:objectOrNil];
- [self.navigationController pushViewController:explorerViewController animated:YES];
+- (NSString *)fieldDescription {
+ if (self.property) {
+ return self.property.fullDescription;
} else {
- // If we didn't get a returned object but the method call succeeded,
- // pop this view controller off the stack to indicate that the call went through.
- [self.navigationController popViewControllerAnimated:YES];
+ return self.ivar.description;
}
}
++ (BOOL)canEditProperty:(FLEXProperty *)property onObject:(id)object currentValue:(id)value {
+ const FLEXTypeEncoding *typeEncoding = property.attributes.typeEncoding.UTF8String;
+ BOOL canEditType = [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:typeEncoding currentValue:value];
+ return canEditType && [object respondsToSelector:property.likelySetter];
+}
+
++ (BOOL)canEditIvar:(Ivar)ivar currentValue:(id)value {
+ return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:ivar_getTypeEncoding(ivar) currentValue:value];
+}
+
@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXIvarEditorViewController.h b/xcode/Pods/FLEX/Classes/Editing/FLEXIvarEditorViewController.h
deleted file mode 100644
index 8fc505ba..00000000
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXIvarEditorViewController.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// FLEXIvarEditorViewController.h
-// Flipboard
-//
-// Created by Ryan Olson on 5/23/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
-//
-
-#import "FLEXMutableFieldEditorViewController.h"
-#import
-
-@interface FLEXIvarEditorViewController : FLEXMutableFieldEditorViewController
-
-- (id)initWithTarget:(id)target ivar:(Ivar)ivar;
-
-+ (BOOL)canEditIvar:(Ivar)ivar currentValue:(id)value;
-
-@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXIvarEditorViewController.m b/xcode/Pods/FLEX/Classes/Editing/FLEXIvarEditorViewController.m
deleted file mode 100644
index bddcaa82..00000000
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXIvarEditorViewController.m
+++ /dev/null
@@ -1,86 +0,0 @@
-//
-// FLEXIvarEditorViewController.m
-// Flipboard
-//
-// Created by Ryan Olson on 5/23/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
-//
-
-#import "FLEXIvarEditorViewController.h"
-#import "FLEXFieldEditorView.h"
-#import "FLEXRuntimeUtility.h"
-#import "FLEXArgumentInputView.h"
-#import "FLEXArgumentInputViewFactory.h"
-#import "FLEXArgumentInputSwitchView.h"
-
-@interface FLEXIvarEditorViewController ()
-
-@property (nonatomic) Ivar ivar;
-
-@end
-
-@implementation FLEXIvarEditorViewController
-
-- (id)initWithTarget:(id)target ivar:(Ivar)ivar
-{
- self = [super initWithTarget:target];
- if (self) {
- self.ivar = ivar;
- self.title = @"Instance Variable";
- }
- return self;
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
-
- self.fieldEditorView.fieldDescription = [FLEXRuntimeUtility prettyNameForIvar:self.ivar];
-
- FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:ivar_getTypeEncoding(self.ivar)];
- inputView.backgroundColor = self.view.backgroundColor;
- inputView.inputValue = [FLEXRuntimeUtility valueForIvar:self.ivar onObject:self.target];
- inputView.delegate = self;
- self.fieldEditorView.argumentInputViews = @[inputView];
-
- // Don't show a "set" button for switches. Set the ivar when the switch toggles.
- if ([inputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
- self.navigationItem.rightBarButtonItem = nil;
- }
-}
-
-- (void)actionButtonPressed:(id)sender
-{
- [super actionButtonPressed:sender];
-
- // TODO: check mutability and use mutableCopy if necessary;
- // this currently could and would assign NSArray to NSMutableArray
-
- [FLEXRuntimeUtility setValue:self.firstInputView.inputValue forIvar:self.ivar onObject:self.target];
- self.firstInputView.inputValue = [FLEXRuntimeUtility valueForIvar:self.ivar onObject:self.target];
-
- // Pop view controller for consistency;
- // property setters and method calls also pop on success.
- [self.navigationController popViewControllerAnimated:YES];
-}
-
-- (void)getterButtonPressed:(id)sender
-{
- [super getterButtonPressed:sender];
- id returnedObject = [FLEXRuntimeUtility valueForIvar:self.ivar onObject:self.target];
- [self exploreObjectOrPopViewController:returnedObject];
-}
-
-- (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)argumentInputView
-{
- if ([argumentInputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
- [self actionButtonPressed:nil];
- }
-}
-
-+ (BOOL)canEditIvar:(Ivar)ivar currentValue:(id)value
-{
- return [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:ivar_getTypeEncoding(ivar) currentValue:value];
-}
-
-@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXMethodCallingViewController.h b/xcode/Pods/FLEX/Classes/Editing/FLEXMethodCallingViewController.h
index 66e73607..6ae928f5 100644
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXMethodCallingViewController.h
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXMethodCallingViewController.h
@@ -3,14 +3,14 @@
// Flipboard
//
// Created by Ryan Olson on 5/23/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
-#import "FLEXFieldEditorViewController.h"
-#import
+#import "FLEXVariableEditorViewController.h"
+#import "FLEXMethod.h"
-@interface FLEXMethodCallingViewController : FLEXFieldEditorViewController
+@interface FLEXMethodCallingViewController : FLEXVariableEditorViewController
-- (id)initWithTarget:(id)target method:(Method)method;
++ (instancetype)target:(id)target method:(FLEXMethod *)method;
@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXMethodCallingViewController.m b/xcode/Pods/FLEX/Classes/Editing/FLEXMethodCallingViewController.m
index 34bf2909..cc4a3eb4 100644
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXMethodCallingViewController.m
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXMethodCallingViewController.m
@@ -3,7 +3,7 @@
// Flipboard
//
// Created by Ryan Olson on 5/23/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXMethodCallingViewController.h"
@@ -16,93 +16,90 @@
#import "FLEXUtility.h"
@interface FLEXMethodCallingViewController ()
-
-@property (nonatomic) Method method;
-@property (nonatomic) FLEXTypeEncoding *returnType;
-
+@property (nonatomic) FLEXMethod *method;
@end
@implementation FLEXMethodCallingViewController
-- (id)initWithTarget:(id)target method:(Method)method
-{
++ (instancetype)target:(id)target method:(FLEXMethod *)method {
+ return [[self alloc] initWithTarget:target method:method];
+}
+
+- (id)initWithTarget:(id)target method:(FLEXMethod *)method {
+ NSParameterAssert(method.isInstanceMethod == !object_isClass(target));
+
self = [super initWithTarget:target];
if (self) {
self.method = method;
- self.returnType = [FLEXRuntimeUtility returnTypeForMethod:method];
- self.title = [self isClassMethod] ? @"Class Method" : @"Method";;
+ self.title = method.isInstanceMethod ? @"Method: " : @"Class Method: ";
+ self.title = [self.title stringByAppendingString:method.selectorString];
}
+
return self;
}
-- (void)viewDidLoad
-{
+- (void)viewDidLoad {
[super viewDidLoad];
-
- NSString *returnType = @((const char *)self.returnType);
- NSString *methodDescription = [FLEXRuntimeUtility prettyNameForMethod:self.method isClassMethod:[self isClassMethod]];
- NSString *format = @"Signature:\n%@\n\nReturn Type:\n%@";
- NSString *info = [NSString stringWithFormat:format, methodDescription, returnType];
- self.fieldEditorView.fieldDescription = info;
-
- NSArray *methodComponents = [FLEXRuntimeUtility prettyArgumentComponentsForMethod:self.method];
- NSMutableArray *argumentInputViews = [NSMutableArray array];
+
+ self.actionButton.title = @"Call";
+
+ // Configure field editor view
+ self.fieldEditorView.argumentInputViews = [self argumentInputViews];
+ self.fieldEditorView.fieldDescription = [NSString stringWithFormat:
+ @"Signature:\n%@\n\nReturn Type:\n%s",
+ self.method.description, (char *)self.method.returnType
+ ];
+}
+
+- (NSArray *)argumentInputViews {
+ Method method = self.method.objc_method;
+ NSArray *methodComponents = [FLEXRuntimeUtility prettyArgumentComponentsForMethod:method];
+ NSMutableArray *argumentInputViews = [NSMutableArray new];
unsigned int argumentIndex = kFLEXNumberOfImplicitArgs;
+
for (NSString *methodComponent in methodComponents) {
- char *argumentTypeEncoding = method_copyArgumentType(self.method, argumentIndex);
+ char *argumentTypeEncoding = method_copyArgumentType(method, argumentIndex);
FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:argumentTypeEncoding];
free(argumentTypeEncoding);
-
+
inputView.backgroundColor = self.view.backgroundColor;
inputView.title = methodComponent;
[argumentInputViews addObject:inputView];
argumentIndex++;
}
- self.fieldEditorView.argumentInputViews = argumentInputViews;
-}
-
-- (void)dealloc
-{
- free(self.returnType);
- self.returnType = NULL;
-}
-- (BOOL)isClassMethod
-{
- return self.target && self.target == [self.target class];
+ return argumentInputViews;
}
-- (NSString *)titleForActionButton
-{
- return @"Call";
-}
-
-- (void)actionButtonPressed:(id)sender
-{
+- (void)actionButtonPressed:(id)sender {
[super actionButtonPressed:sender];
-
- NSMutableArray *arguments = [NSMutableArray array];
+
+ // Gather arguments
+ NSMutableArray *arguments = [NSMutableArray new];
for (FLEXArgumentInputView *inputView in self.fieldEditorView.argumentInputViews) {
- id argumentValue = inputView.inputValue;
- if (!argumentValue) {
- // Use NSNulls as placeholders in the array. They will be interpreted as nil arguments.
- argumentValue = [NSNull null];
- }
- [arguments addObject:argumentValue];
+ // Use NSNull as a nil placeholder; it will be interpreted as nil
+ [arguments addObject:inputView.inputValue ?: NSNull.null];
}
-
+
+ // Call method
NSError *error = nil;
- id returnedObject = [FLEXRuntimeUtility performSelector:method_getName(self.method) onObject:self.target withArguments:arguments error:&error];
-
+ id returnValue = [FLEXRuntimeUtility
+ performSelector:self.method.selector
+ onObject:self.target
+ withArguments:arguments
+ error:&error
+ ];
+
+ // Display return value or error
if (error) {
- [FLEXAlert showAlert:@"Method Call Failed" message:[error localizedDescription] from:self];
- } else if (returnedObject) {
+ [FLEXAlert showAlert:@"Method Call Failed" message:error.localizedDescription from:self];
+ } else if (returnValue) {
// For non-nil (or void) return types, push an explorer view controller to display the returned object
- returnedObject = [FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:returnedObject type:self.returnType];
- FLEXObjectExplorerViewController *explorerViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:returnedObject];
- [self.navigationController pushViewController:explorerViewController animated:YES];
+ returnValue = [FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:returnValue type:self.method.returnType];
+ FLEXObjectExplorerViewController *explorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:returnValue];
+ [self.navigationController pushViewController:explorer animated:YES];
} else {
- [self exploreObjectOrPopViewController:returnedObject];
+ [self exploreObjectOrPopViewController:returnValue];
}
}
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXMutableFieldEditorViewController.h b/xcode/Pods/FLEX/Classes/Editing/FLEXMutableFieldEditorViewController.h
deleted file mode 100644
index 6ea033c1..00000000
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXMutableFieldEditorViewController.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// FLEXMutableFieldEditorViewController.h
-// FLEX
-//
-// Created by Tanner on 11/22/18.
-// Copyright © 2018 Flipboard. All rights reserved.
-//
-
-#import "FLEXFieldEditorViewController.h"
-
-@interface FLEXMutableFieldEditorViewController : FLEXFieldEditorViewController
-
-@property (nonatomic, readonly) UIBarButtonItem *getterButton;
-
-- (void)getterButtonPressed:(id)sender;
-- (NSString *)titleForGetterButton;
-
-@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXMutableFieldEditorViewController.m b/xcode/Pods/FLEX/Classes/Editing/FLEXMutableFieldEditorViewController.m
deleted file mode 100644
index 65dcd615..00000000
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXMutableFieldEditorViewController.m
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// FLEXMutableFieldEditorViewController.m
-// FLEX
-//
-// Created by Tanner on 11/22/18.
-// Copyright © 2018 Flipboard. All rights reserved.
-//
-
-#import "FLEXMutableFieldEditorViewController.h"
-#import "FLEXFieldEditorView.h"
-
-@interface FLEXMutableFieldEditorViewController ()
-
-@property (nonatomic, readwrite) UIBarButtonItem *getterButton;
-
-@end
-
-@implementation FLEXMutableFieldEditorViewController
-
-- (void)viewDidLoad {
- [super viewDidLoad];
-
- self.getterButton = [[UIBarButtonItem alloc] initWithTitle:[self titleForGetterButton] style:UIBarButtonItemStyleDone target:self action:@selector(getterButtonPressed:)];
- self.navigationItem.rightBarButtonItems = @[self.setterButton, self.getterButton];
-}
-
-- (void)getterButtonPressed:(id)sender {
- // Subclasses can override
- [self.fieldEditorView endEditing:YES];
-}
-
-- (NSString *)titleForGetterButton {
- return @"Get";
-}
-
-@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXPropertyEditorViewController.h b/xcode/Pods/FLEX/Classes/Editing/FLEXPropertyEditorViewController.h
deleted file mode 100644
index 1914a993..00000000
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXPropertyEditorViewController.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// FLEXPropertyEditorViewController.h
-// Flipboard
-//
-// Created by Ryan Olson on 5/20/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
-//
-
-#import "FLEXMutableFieldEditorViewController.h"
-#import
-
-@interface FLEXPropertyEditorViewController : FLEXMutableFieldEditorViewController
-
-- (id)initWithTarget:(id)target property:(objc_property_t)property;
-
-+ (BOOL)canEditProperty:(objc_property_t)property onObject:(id)object currentValue:(id)value;
-
-@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXPropertyEditorViewController.m b/xcode/Pods/FLEX/Classes/Editing/FLEXPropertyEditorViewController.m
deleted file mode 100644
index 303d1d82..00000000
--- a/xcode/Pods/FLEX/Classes/Editing/FLEXPropertyEditorViewController.m
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-// FLEXPropertyEditorViewController.m
-// Flipboard
-//
-// Created by Ryan Olson on 5/20/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
-//
-
-#import "FLEXPropertyEditorViewController.h"
-#import "FLEXRuntimeUtility.h"
-#import "FLEXFieldEditorView.h"
-#import "FLEXArgumentInputView.h"
-#import "FLEXArgumentInputViewFactory.h"
-#import "FLEXArgumentInputSwitchView.h"
-#import "FLEXUtility.h"
-
-@interface FLEXPropertyEditorViewController ()
-
-@property (nonatomic) objc_property_t property;
-
-@end
-
-@implementation FLEXPropertyEditorViewController
-
-- (id)initWithTarget:(id)target property:(objc_property_t)property
-{
- self = [super initWithTarget:target];
- if (self) {
- self.property = property;
- self.title = @"Property";
- }
- return self;
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
-
- self.fieldEditorView.fieldDescription = [FLEXRuntimeUtility fullDescriptionForProperty:self.property];
- id currentValue = [FLEXRuntimeUtility valueForProperty:self.property onObject:self.target];
- self.setterButton.enabled = [[self class] canEditProperty:self.property onObject:self.target currentValue:currentValue];
-
- const char *typeEncoding = [FLEXRuntimeUtility typeEncodingForProperty:self.property].UTF8String;
- FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:typeEncoding];
- inputView.backgroundColor = self.view.backgroundColor;
- inputView.inputValue = [FLEXRuntimeUtility valueForProperty:self.property onObject:self.target];
- inputView.delegate = self;
- self.fieldEditorView.argumentInputViews = @[inputView];
-
- // Don't show a "set" button for switches - just call the setter immediately after the switch toggles.
- if ([inputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
- self.navigationItem.rightBarButtonItem = nil;
- }
-}
-
-- (void)actionButtonPressed:(id)sender
-{
- [super actionButtonPressed:sender];
-
- id userInputObject = self.firstInputView.inputValue;
- NSArray *arguments = userInputObject ? @[userInputObject] : nil;
- SEL setterSelector = [FLEXRuntimeUtility setterSelectorForProperty:self.property];
- NSError *error = nil;
- [FLEXRuntimeUtility performSelector:setterSelector onObject:self.target withArguments:arguments error:&error];
- if (error) {
- [FLEXAlert showAlert:@"Property Setter Failed" message:[error localizedDescription] from:self];
- self.firstInputView.inputValue = [FLEXRuntimeUtility valueForProperty:self.property onObject:self.target];
- } else {
- // If the setter was called without error, pop the view controller to indicate that and make the user's life easier.
- // Don't do this for simulated taps on the action button (i.e. from switch/BOOL editors). The experience is weird there.
- if (sender) {
- [self.navigationController popViewControllerAnimated:YES];
- }
- }
-}
-
-- (void)getterButtonPressed:(id)sender
-{
- [super getterButtonPressed:sender];
- id returnedObject = [FLEXRuntimeUtility valueForProperty:self.property onObject:self.target];
- [self exploreObjectOrPopViewController:returnedObject];
-}
-
-- (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)argumentInputView
-{
- if ([argumentInputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
- [self actionButtonPressed:nil];
- }
-}
-
-+ (BOOL)canEditProperty:(objc_property_t)property onObject:(id)object currentValue:(id)value
-{
- const char *typeEncoding = [FLEXRuntimeUtility typeEncodingForProperty:property].UTF8String;
- BOOL canEditType = [FLEXArgumentInputViewFactory canEditFieldWithTypeEncoding:typeEncoding currentValue:value];
- SEL setterSelector = [FLEXRuntimeUtility setterSelectorForProperty:property];
- BOOL isReadonly = [FLEXRuntimeUtility isReadonlyProperty:property] && (!setterSelector || ![object respondsToSelector:setterSelector]);
- return canEditType && !isReadonly;
-}
-
-@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXVariableEditorViewController.h b/xcode/Pods/FLEX/Classes/Editing/FLEXVariableEditorViewController.h
new file mode 100644
index 00000000..045ba7d4
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXVariableEditorViewController.h
@@ -0,0 +1,35 @@
+//
+// FLEXVariableEditorViewController.h
+// Flipboard
+//
+// Created by Ryan Olson on 5/16/14.
+// Copyright (c) 2020 Flipboard. All rights reserved.
+//
+
+#import
+
+@class FLEXFieldEditorView;
+@class FLEXArgumentInputView;
+
+/// Provides a screen for editing or configuring one or more variables.
+@interface FLEXVariableEditorViewController : UIViewController
+
++ (instancetype)target:(id)target;
+- (id)initWithTarget:(id)target;
+
+// Convenience accessor since many subclasses only use one input view
+@property (nonatomic, readonly) FLEXArgumentInputView *firstInputView;
+
+// For subclass use only.
+@property (nonatomic, readonly) id target;
+@property (nonatomic, readonly) FLEXFieldEditorView *fieldEditorView;
+/// Subclasses can change the button title via the button's \c title property
+@property (nonatomic, readonly) UIBarButtonItem *actionButton;
+
+- (void)actionButtonPressed:(id)sender;
+
+/// Pushes an explorer view controller for the given object
+/// or pops the current view controller.
+- (void)exploreObjectOrPopViewController:(id)objectOrNil;
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/Editing/FLEXVariableEditorViewController.m b/xcode/Pods/FLEX/Classes/Editing/FLEXVariableEditorViewController.m
new file mode 100644
index 00000000..c2d29d9c
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/Editing/FLEXVariableEditorViewController.m
@@ -0,0 +1,137 @@
+//
+// FLEXVariableEditorViewController.m
+// Flipboard
+//
+// Created by Ryan Olson on 5/16/14.
+// Copyright (c) 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXColor.h"
+#import "FLEXVariableEditorViewController.h"
+#import "FLEXFieldEditorView.h"
+#import "FLEXRuntimeUtility.h"
+#import "FLEXUtility.h"
+#import "FLEXObjectExplorerFactory.h"
+#import "FLEXArgumentInputView.h"
+#import "FLEXArgumentInputViewFactory.h"
+#import "FLEXObjectExplorerViewController.h"
+#import "UIBarButtonItem+FLEX.h"
+
+@interface FLEXVariableEditorViewController ()
+@property (nonatomic) UIScrollView *scrollView;
+@property (nonatomic) id target;
+@end
+
+@implementation FLEXVariableEditorViewController
+
+#pragma mark - Initialization
+
++ (instancetype)target:(id)target {
+ return [[self alloc] initWithTarget:target];
+}
+
+- (id)initWithTarget:(id)target {
+ self = [super init];
+ if (self) {
+ self.target = target;
+ [NSNotificationCenter.defaultCenter
+ addObserver:self selector:@selector(keyboardDidShow:)
+ name:UIKeyboardDidShowNotification object:nil
+ ];
+ [NSNotificationCenter.defaultCenter
+ addObserver:self selector:@selector(keyboardWillHide:)
+ name:UIKeyboardWillHideNotification object:nil
+ ];
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [NSNotificationCenter.defaultCenter removeObserver:self];
+}
+
+#pragma mark - UIViewController methods
+
+- (void)keyboardDidShow:(NSNotification *)notification {
+ CGRect keyboardRectInWindow = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
+ CGSize keyboardSize = [self.view convertRect:keyboardRectInWindow fromView:nil].size;
+ UIEdgeInsets scrollInsets = self.scrollView.contentInset;
+ scrollInsets.bottom = keyboardSize.height;
+ self.scrollView.contentInset = scrollInsets;
+ self.scrollView.scrollIndicatorInsets = scrollInsets;
+
+ // Find the active input view and scroll to make sure it's visible.
+ for (FLEXArgumentInputView *argumentInputView in self.fieldEditorView.argumentInputViews) {
+ if (argumentInputView.inputViewIsFirstResponder) {
+ CGRect scrollToVisibleRect = [self.scrollView convertRect:argumentInputView.bounds fromView:argumentInputView];
+ [self.scrollView scrollRectToVisible:scrollToVisibleRect animated:YES];
+ break;
+ }
+ }
+}
+
+- (void)keyboardWillHide:(NSNotification *)notification {
+ UIEdgeInsets scrollInsets = self.scrollView.contentInset;
+ scrollInsets.bottom = 0.0;
+ self.scrollView.contentInset = scrollInsets;
+ self.scrollView.scrollIndicatorInsets = scrollInsets;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.view.backgroundColor = FLEXColor.scrollViewBackgroundColor;
+
+ self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
+ self.scrollView.backgroundColor = self.view.backgroundColor;
+ self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ self.scrollView.delegate = self;
+ [self.view addSubview:self.scrollView];
+
+ _fieldEditorView = [FLEXFieldEditorView new];
+ self.fieldEditorView.targetDescription = [NSString stringWithFormat:@"%@ %p", [self.target class], self.target];
+ [self.scrollView addSubview:self.fieldEditorView];
+
+ _actionButton = [[UIBarButtonItem alloc]
+ initWithTitle:@"Set"
+ style:UIBarButtonItemStyleDone
+ target:self
+ action:@selector(actionButtonPressed:)
+ ];
+
+ self.navigationController.toolbarHidden = NO;
+ self.toolbarItems = @[UIBarButtonItem.flex_flexibleSpace, self.actionButton];
+}
+
+- (void)viewWillLayoutSubviews {
+ CGSize constrainSize = CGSizeMake(self.scrollView.bounds.size.width, CGFLOAT_MAX);
+ CGSize fieldEditorSize = [self.fieldEditorView sizeThatFits:constrainSize];
+ self.fieldEditorView.frame = CGRectMake(0, 0, fieldEditorSize.width, fieldEditorSize.height);
+ self.scrollView.contentSize = fieldEditorSize;
+}
+
+#pragma mark - Public
+
+- (FLEXArgumentInputView *)firstInputView {
+ return [self.fieldEditorView argumentInputViews].firstObject;
+}
+
+- (void)actionButtonPressed:(id)sender {
+ // Subclasses can override
+ [self.fieldEditorView endEditing:YES];
+}
+
+- (void)exploreObjectOrPopViewController:(id)objectOrNil {
+ if (objectOrNil) {
+ // For non-nil (or void) return types, push an explorer view controller to display the object
+ FLEXObjectExplorerViewController *explorerViewController = [FLEXObjectExplorerFactory explorerViewControllerForObject:objectOrNil];
+ [self.navigationController pushViewController:explorerViewController animated:YES];
+ } else {
+ // If we didn't get a returned object but the method call succeeded,
+ // pop this view controller off the stack to indicate that the call went through.
+ [self.navigationController popViewControllerAnimated:YES];
+ }
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarkManager.h b/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarkManager.h
new file mode 100644
index 00000000..4a0b9036
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarkManager.h
@@ -0,0 +1,19 @@
+//
+// FLEXBookmarkManager.h
+// FLEX
+//
+// Created by Tanner on 2/6/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FLEXBookmarkManager : NSObject
+
+@property (nonatomic, readonly, class) NSMutableArray *bookmarks;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarkManager.m b/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarkManager.m
new file mode 100644
index 00000000..58229dcd
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarkManager.m
@@ -0,0 +1,25 @@
+//
+// FLEXBookmarkManager.m
+// FLEX
+//
+// Created by Tanner on 2/6/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXBookmarkManager.h"
+
+static NSMutableArray *kFLEXBookmarkManagerBookmarks = nil;
+
+@implementation FLEXBookmarkManager
+
++ (void)initialize {
+ if (self == [FLEXBookmarkManager class]) {
+ kFLEXBookmarkManagerBookmarks = [NSMutableArray new];
+ }
+}
+
++ (NSMutableArray *)bookmarks {
+ return kFLEXBookmarkManagerBookmarks;
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.h b/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.h
new file mode 100644
index 00000000..207f206f
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.h
@@ -0,0 +1,17 @@
+//
+// FLEXBookmarksViewController.h
+// FLEX
+//
+// Created by Tanner on 2/6/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FLEXBookmarksViewController : FLEXTableViewController
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.m b/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.m
new file mode 100644
index 00000000..36e21920
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/Bookmarks/FLEXBookmarksViewController.m
@@ -0,0 +1,235 @@
+//
+// FLEXBookmarksViewController.m
+// FLEX
+//
+// Created by Tanner on 2/6/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXBookmarksViewController.h"
+#import "FLEXExplorerViewController.h"
+#import "FLEXNavigationController.h"
+#import "FLEXObjectExplorerFactory.h"
+#import "FLEXBookmarkManager.h"
+#import "UIBarButtonItem+FLEX.h"
+#import "FLEXColor.h"
+#import "FLEXUtility.h"
+#import "FLEXRuntimeUtility.h"
+#import "FLEXTableView.h"
+
+@interface FLEXBookmarksViewController ()
+@property (nonatomic, copy) NSArray *bookmarks;
+@property (nonatomic, readonly) FLEXExplorerViewController *corePresenter;
+@end
+
+@implementation FLEXBookmarksViewController
+
+#pragma mark - Initialization
+
+- (id)init {
+ return [self initWithStyle:UITableViewStylePlain];
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.navigationController.hidesBarsOnSwipe = NO;
+ self.tableView.allowsMultipleSelectionDuringEditing = YES;
+
+ [self reloadData];
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+ [self setupDefaultBarItems];
+}
+
+
+#pragma mark - Private
+
+- (void)reloadData {
+ // We assume the bookmarks aren't going to change out from under us, since
+ // presenting any other tool via keyboard shortcuts should dismiss us first
+ self.bookmarks = FLEXBookmarkManager.bookmarks;
+ self.title = [NSString stringWithFormat:@"Bookmarks (%@)", @(self.bookmarks.count)];
+}
+
+- (void)setupDefaultBarItems {
+ self.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(Done, self, @selector(dismissAnimated));
+ self.toolbarItems = @[
+ UIBarButtonItem.flex_flexibleSpace,
+ FLEXBarButtonItemSystem(Edit, self, @selector(toggleEditing)),
+ ];
+
+ // Disable editing if no bookmarks available
+ self.toolbarItems.lastObject.enabled = self.bookmarks.count > 0;
+}
+
+- (void)setupEditingBarItems {
+ self.navigationItem.rightBarButtonItem = nil;
+ self.toolbarItems = @[
+ [UIBarButtonItem itemWithTitle:@"Close All" target:self action:@selector(closeAllButtonPressed:)],
+ UIBarButtonItem.flex_flexibleSpace,
+ // We use a non-system done item because we change its title dynamically
+ [UIBarButtonItem doneStyleitemWithTitle:@"Done" target:self action:@selector(toggleEditing)]
+ ];
+
+ self.toolbarItems.firstObject.tintColor = FLEXColor.destructiveColor;
+}
+
+- (FLEXExplorerViewController *)corePresenter {
+ // We must be presented by a FLEXExplorerViewController, or presented
+ // by another view controller that was presented by FLEXExplorerViewController
+ FLEXExplorerViewController *presenter = (id)self.presentingViewController;
+ presenter = (id)presenter.presentingViewController ?: presenter;
+ presenter = (id)presenter.presentingViewController ?: presenter;
+ NSAssert(
+ [presenter isKindOfClass:[FLEXExplorerViewController class]],
+ @"The bookmarks view controller expects to be presented by the explorer controller"
+ );
+ return presenter;
+}
+
+#pragma mark Button Actions
+
+- (void)dismissAnimated {
+ [self dismissAnimated:nil];
+}
+
+- (void)dismissAnimated:(id)selectedObject {
+ if (selectedObject) {
+ UIViewController *explorer = [FLEXObjectExplorerFactory
+ explorerViewControllerForObject:selectedObject
+ ];
+ if ([self.presentingViewController isKindOfClass:[FLEXNavigationController class]]) {
+ // I am presented on an existing navigation stack, so
+ // dismiss myself and push the bookmark there
+ UINavigationController *presenter = (id)self.presentingViewController;
+ [presenter dismissViewControllerAnimated:YES completion:^{
+ [presenter pushViewController:explorer animated:YES];
+ }];
+ } else {
+ // Dismiss myself and present explorer
+ UIViewController *presenter = self.corePresenter;
+ [presenter dismissViewControllerAnimated:YES completion:^{
+ [presenter presentViewController:[FLEXNavigationController
+ withRootViewController:explorer
+ ] animated:YES completion:nil];
+ }];
+ }
+ } else {
+ // Just dismiss myself
+ [self dismissViewControllerAnimated:YES completion:nil];
+ }
+}
+
+- (void)toggleEditing {
+ NSArray *selected = self.tableView.indexPathsForSelectedRows;
+ self.editing = !self.editing;
+
+ if (self.isEditing) {
+ [self setupEditingBarItems];
+ } else {
+ [self setupDefaultBarItems];
+
+ // Get index set of bookmarks to close
+ NSMutableIndexSet *indexes = [NSMutableIndexSet new];
+ for (NSIndexPath *ip in selected) {
+ [indexes addIndex:ip.row];
+ }
+
+ if (selected.count) {
+ // Close bookmarks and update data source
+ [FLEXBookmarkManager.bookmarks removeObjectsAtIndexes:indexes];
+ [self reloadData];
+
+ // Remove deleted rows
+ [self.tableView deleteRowsAtIndexPaths:selected withRowAnimation:UITableViewRowAnimationAutomatic];
+ }
+ }
+}
+
+- (void)closeAllButtonPressed:(UIBarButtonItem *)sender {
+ [FLEXAlert makeSheet:^(FLEXAlert *make) {
+ NSInteger count = self.bookmarks.count;
+ NSString *title = FLEXPluralFormatString(count, @"Remove %@ bookmarks", @"Remove %@ bookmark");
+ make.button(title).destructiveStyle().handler(^(NSArray *strings) {
+ [self closeAll];
+ [self toggleEditing];
+ });
+ make.button(@"Cancel").cancelStyle();
+ } showFrom:self source:sender];
+}
+
+- (void)closeAll {
+ NSInteger rowCount = self.bookmarks.count;
+
+ // Close bookmarks and update data source
+ [FLEXBookmarkManager.bookmarks removeAllObjects];
+ [self reloadData];
+
+ // Delete rows from table view
+ NSArray *allRows = [NSArray flex_forEachUpTo:rowCount map:^id(NSUInteger row) {
+ return [NSIndexPath indexPathForRow:row inSection:0];
+ }];
+ [self.tableView deleteRowsAtIndexPaths:allRows withRowAnimation:UITableViewRowAnimationAutomatic];
+}
+
+
+#pragma mark - Table View Data Source
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return self.bookmarks.count;
+}
+
+- (UITableViewCell *)tableView:(FLEXTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath];
+
+ id object = self.bookmarks[indexPath.row];
+ cell.textLabel.text = [FLEXRuntimeUtility safeDescriptionForObject:object];
+ cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ — %p", [object class], object];
+
+ return cell;
+}
+
+
+#pragma mark - Table View Delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ if (self.editing) {
+ // Case: editing with multi-select
+ self.toolbarItems.lastObject.title = @"Remove Selected";
+ self.toolbarItems.lastObject.tintColor = FLEXColor.destructiveColor;
+ } else {
+ // Case: selected a bookmark
+ [self dismissAnimated:self.bookmarks[indexPath.row]];
+ }
+}
+
+- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
+ NSParameterAssert(self.editing);
+
+ if (tableView.indexPathsForSelectedRows.count == 0) {
+ self.toolbarItems.lastObject.title = @"Done";
+ self.toolbarItems.lastObject.tintColor = self.view.tintColor;
+ }
+}
+
+- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
+ return YES;
+}
+
+- (void)tableView:(UITableView *)table
+commitEditingStyle:(UITableViewCellEditingStyle)edit
+forRowAtIndexPath:(NSIndexPath *)indexPath {
+ NSParameterAssert(edit == UITableViewCellEditingStyleDelete);
+
+ // Remove bookmark and update data source
+ [FLEXBookmarkManager.bookmarks removeObjectAtIndex:indexPath.row];
+ [self reloadData];
+
+ // Delete row from table view
+ [table deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXExplorerViewController.h b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXExplorerViewController.h
index 2ff99a98..d4bedb31 100644
--- a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXExplorerViewController.h
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXExplorerViewController.h
@@ -3,26 +3,29 @@
// Flipboard
//
// Created by Ryan Olson on 4/4/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
-#import
+#import "FLEXExplorerToolbar.h"
+@class FLEXWindow;
@protocol FLEXExplorerViewControllerDelegate;
/// A view controller that manages the FLEX toolbar.
@interface FLEXExplorerViewController : UIViewController
@property (nonatomic, weak) id delegate;
+@property (nonatomic, readonly) BOOL wantsWindowToBecomeKey;
+
+@property (nonatomic, readonly) FLEXExplorerToolbar *explorerToolbar;
- (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates;
-- (BOOL)wantsWindowToBecomeKey;
/// @brief Used to present (or dismiss) a modal view controller ("tool"), typically triggered by pressing a button in the toolbar.
///
/// If a tool is already presented, this method simply dismisses it and calls the completion block.
/// If no tool is presented, @code future() @endcode is presented and the completion block is called.
-- (void)toggleToolWithViewControllerProvider:(UIViewController *(^)(void))future completion:(void(^)(void))completion;
+- (void)toggleToolWithViewControllerProvider:(UINavigationController *(^)(void))future completion:(void(^)(void))completion;
// Keyboard shortcut helpers
@@ -30,15 +33,19 @@
- (void)toggleMoveTool;
- (void)toggleViewsTool;
- (void)toggleMenuTool;
-- (void)handleDownArrowKeyPressed;
-- (void)handleUpArrowKeyPressed;
-- (void)handleRightArrowKeyPressed;
-- (void)handleLeftArrowKeyPressed;
+
+/// @return YES if the explorer used the key press to perform an action, NO otherwise
+- (BOOL)handleDownArrowKeyPressed;
+/// @return YES if the explorer used the key press to perform an action, NO otherwise
+- (BOOL)handleUpArrowKeyPressed;
+/// @return YES if the explorer used the key press to perform an action, NO otherwise
+- (BOOL)handleRightArrowKeyPressed;
+/// @return YES if the explorer used the key press to perform an action, NO otherwise
+- (BOOL)handleLeftArrowKeyPressed;
@end
+#pragma mark -
@protocol FLEXExplorerViewControllerDelegate
-
- (void)explorerViewControllerDidFinish:(FLEXExplorerViewController *)explorerViewController;
-
@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXExplorerViewController.m b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXExplorerViewController.m
index 02752f44..b966795d 100644
--- a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXExplorerViewController.m
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXExplorerViewController.m
@@ -3,20 +3,24 @@
// Flipboard
//
// Created by Ryan Olson on 4/4/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXExplorerViewController.h"
-#import "FLEXExplorerToolbar.h"
-#import "FLEXToolbarItem.h"
+#import "FLEXExplorerToolbarItem.h"
#import "FLEXUtility.h"
-#import "FLEXHierarchyTableViewController.h"
-#import "FLEXGlobalsTableViewController.h"
+#import "FLEXWindow.h"
+#import "FLEXTabList.h"
+#import "FLEXNavigationController.h"
+#import "FLEXHierarchyViewController.h"
+#import "FLEXGlobalsViewController.h"
#import "FLEXObjectExplorerViewController.h"
#import "FLEXObjectExplorerFactory.h"
-#import "FLEXNetworkHistoryTableViewController.h"
-
-static NSString *const kFLEXToolbarTopMarginDefaultsKey = @"com.flex.FLEXToolbar.topMargin";
+#import "FLEXNetworkMITMViewController.h"
+#import "FLEXTabsViewController.h"
+#import "FLEXWindowManagerController.h"
+#import "FLEXViewControllersViewController.h"
+#import "NSUserDefaults+FLEX.h"
typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
FLEXExplorerModeDefault,
@@ -24,9 +28,7 @@ typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
FLEXExplorerModeMove
};
-@interface FLEXExplorerViewController ()
-
-@property (nonatomic) FLEXExplorerToolbar *explorerToolbar;
+@interface FLEXExplorerViewController ()
/// Tracks the currently active tool/mode
@property (nonatomic) FLEXExplorerMode currentMode;
@@ -56,55 +58,60 @@ @interface FLEXExplorerViewController () *observedViews;
+/// Used to preserve the target app's UIMenuController items.
+@property (nonatomic) NSArray *appMenuItems;
+
@end
@implementation FLEXExplorerViewController
-- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
-{
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
- self.observedViews = [NSMutableSet set];
+ self.observedViews = [NSMutableSet new];
}
return self;
}
--(void)dealloc
-{
+- (void)dealloc {
for (UIView *view in _observedViews) {
[self stopObservingView:view];
}
}
-- (void)viewDidLoad
-{
+- (void)viewDidLoad {
[super viewDidLoad];
// Toolbar
- self.explorerToolbar = [FLEXExplorerToolbar new];
+ _explorerToolbar = [FLEXExplorerToolbar new];
// Start the toolbar off below any bars that may be at the top of the view.
- id toolbarOriginYDefault = [[NSUserDefaults standardUserDefaults] objectForKey:kFLEXToolbarTopMarginDefaultsKey];
- CGFloat toolbarOriginY = toolbarOriginYDefault ? [toolbarOriginYDefault doubleValue] : 100;
+ CGFloat toolbarOriginY = NSUserDefaults.standardUserDefaults.flex_toolbarTopMargin;
CGRect safeArea = [self viewSafeArea];
- CGSize toolbarSize = [self.explorerToolbar sizeThatFits:CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(safeArea))];
- [self updateToolbarPositionWithUnconstrainedFrame:CGRectMake(CGRectGetMinX(safeArea), toolbarOriginY, toolbarSize.width, toolbarSize.height)];
- self.explorerToolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;
+ CGSize toolbarSize = [self.explorerToolbar sizeThatFits:CGSizeMake(
+ CGRectGetWidth(self.view.bounds), CGRectGetHeight(safeArea)
+ )];
+ [self updateToolbarPositionWithUnconstrainedFrame:CGRectMake(
+ CGRectGetMinX(safeArea), toolbarOriginY, toolbarSize.width, toolbarSize.height
+ )];
+ self.explorerToolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth |
+ UIViewAutoresizingFlexibleBottomMargin |
+ UIViewAutoresizingFlexibleTopMargin;
[self.view addSubview:self.explorerToolbar];
[self setupToolbarActions];
[self setupToolbarGestures];
// View selection
- UITapGestureRecognizer *selectionTapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSelectionTap:)];
+ UITapGestureRecognizer *selectionTapGR = [[UITapGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleSelectionTap:)
+ ];
[self.view addGestureRecognizer:selectionTapGR];
// View moving
@@ -113,8 +120,7 @@ - (void)viewDidLoad
[self.view addGestureRecognizer:self.movePanGR];
}
-- (void)viewWillAppear:(BOOL)animated
-{
+- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self updateButtonStates];
@@ -123,32 +129,35 @@ - (void)viewWillAppear:(BOOL)animated
#pragma mark - Rotation
-- (UIViewController *)viewControllerForRotationAndOrientation
-{
- UIWindow *window = self.previousKeyWindow ?: [UIApplication.sharedApplication keyWindow];
- UIViewController *viewController = window.rootViewController;
+- (UIViewController *)viewControllerForRotationAndOrientation {
+ UIViewController *viewController = FLEXUtility.appKeyWindow.rootViewController;
// Obfuscating selector _viewControllerForSupportedInterfaceOrientations
- NSString *viewControllerSelectorString = [@[@"_vie", @"wContro", @"llerFor", @"Supported", @"Interface", @"Orientations"] componentsJoinedByString:@""];
+ NSString *viewControllerSelectorString = [@[
+ @"_vie", @"wContro", @"llerFor", @"Supported", @"Interface", @"Orientations"
+ ] componentsJoinedByString:@""];
SEL viewControllerSelector = NSSelectorFromString(viewControllerSelectorString);
if ([viewController respondsToSelector:viewControllerSelector]) {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- viewController = [viewController performSelector:viewControllerSelector];
-#pragma clang diagnostic pop
+ viewController = [viewController valueForKey:viewControllerSelectorString];
}
+
return viewController;
}
-- (UIInterfaceOrientationMask)supportedInterfaceOrientations
-{
+- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
+ // Commenting this out until I can figure out a better way to solve this
+// if (self.window.isKeyWindow) {
+// [self.window resignKeyWindow];
+// }
+
UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation];
- UIInterfaceOrientationMask supportedOrientations = [FLEXUtility infoPlistSupportedInterfaceOrientationsMask];
- if (viewControllerToAsk && viewControllerToAsk != self) {
+ UIInterfaceOrientationMask supportedOrientations = FLEXUtility.infoPlistSupportedInterfaceOrientationsMask;
+ if (viewControllerToAsk && ![viewControllerToAsk isKindOfClass:[self class]]) {
supportedOrientations = [viewControllerToAsk supportedInterfaceOrientations];
}
// The UIViewController docs state that this method must not return zero.
- // If we weren't able to get a valid value for the supported interface orientations, default to all supported.
+ // If we weren't able to get a valid value for the supported interface
+ // orientations, default to all supported.
if (supportedOrientations == 0) {
supportedOrientations = UIInterfaceOrientationMaskAll;
}
@@ -156,8 +165,7 @@ - (UIInterfaceOrientationMask)supportedInterfaceOrientations
return supportedOrientations;
}
-- (BOOL)shouldAutorotate
-{
+- (BOOL)shouldAutorotate {
UIViewController *viewControllerToAsk = [self viewControllerForRotationAndOrientation];
BOOL shouldAutorotate = YES;
if (viewControllerToAsk && viewControllerToAsk != self) {
@@ -166,8 +174,9 @@ - (BOOL)shouldAutorotate
return shouldAutorotate;
}
-- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator
-{
+- (void)viewWillTransitionToSize:(CGSize)size
+ withTransitionCoordinator:(id)coordinator {
+ [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[coordinator animateAlongsideTransition:^(id context) {
for (UIView *outlineView in self.outlineViewsForVisibleViews.allValues) {
outlineView.hidden = YES;
@@ -190,10 +199,10 @@ - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id *)viewsAtTapPoint
-{
+- (void)setViewsAtTapPoint:(NSArray *)viewsAtTapPoint {
if (![_viewsAtTapPoint isEqual:viewsAtTapPoint]) {
for (UIView *view in _viewsAtTapPoint) {
if (view != self.selectedView) {
@@ -248,8 +261,7 @@ - (void)setViewsAtTapPoint:(NSArray *)viewsAtTapPoint
}
}
-- (void)setCurrentMode:(FLEXExplorerMode)currentMode
-{
+- (void)setCurrentMode:(FLEXExplorerMode)currentMode {
if (_currentMode != currentMode) {
_currentMode = currentMode;
switch (currentMode) {
@@ -268,7 +280,8 @@ - (void)setCurrentMode:(FLEXExplorerMode)currentMode
break;
case FLEXExplorerModeMove:
- // Hide all the outline views to focus on the selected view, which is the only one that will move.
+ // Hide all the outline views to focus on the selected view,
+ // which is the only one that will move.
for (NSValue *key in self.outlineViewsForVisibleViews) {
UIView *outlineView = self.outlineViewsForVisibleViews[key];
outlineView.hidden = YES;
@@ -283,35 +296,32 @@ - (void)setCurrentMode:(FLEXExplorerMode)currentMode
#pragma mark - View Tracking
-- (void)beginObservingView:(UIView *)view
-{
+- (void)beginObservingView:(UIView *)view {
// Bail if we're already observing this view or if there's nothing to observe.
if (!view || [self.observedViews containsObject:view]) {
return;
}
- for (NSString *keyPath in [[self class] viewKeyPathsToTrack]) {
+ for (NSString *keyPath in self.viewKeyPathsToTrack) {
[view addObserver:self forKeyPath:keyPath options:0 context:NULL];
}
[self.observedViews addObject:view];
}
-- (void)stopObservingView:(UIView *)view
-{
+- (void)stopObservingView:(UIView *)view {
if (!view) {
return;
}
- for (NSString *keyPath in [[self class] viewKeyPathsToTrack]) {
+ for (NSString *keyPath in self.viewKeyPathsToTrack) {
[view removeObserver:self forKeyPath:keyPath];
}
[self.observedViews removeObject:view];
}
-+ (NSArray *)viewKeyPathsToTrack
-{
+- (NSArray *)viewKeyPathsToTrack {
static NSArray *trackedViewKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@@ -321,13 +331,13 @@ - (void)stopObservingView:(UIView *)view
return trackedViewKeyPaths;
}
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
-{
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
+ change:(NSDictionary *)change
+ context:(void *)context {
[self updateOverlayAndDescriptionForObjectIfNeeded:object];
}
-- (void)updateOverlayAndDescriptionForObjectIfNeeded:(id)object
-{
+- (void)updateOverlayAndDescriptionForObjectIfNeeded:(id)object {
NSUInteger indexOfView = [self.viewsAtTapPoint indexOfObject:object];
if (indexOfView != NSNotFound) {
UIView *view = self.viewsAtTapPoint[indexOfView];
@@ -339,106 +349,135 @@ - (void)updateOverlayAndDescriptionForObjectIfNeeded:(id)object
}
if (object == self.selectedView) {
// Update the selected view description since we show the frame value there.
- self.explorerToolbar.selectedViewDescription = [FLEXUtility descriptionForView:self.selectedView includingFrame:YES];
+ self.explorerToolbar.selectedViewDescription = [FLEXUtility
+ descriptionForView:self.selectedView includingFrame:YES
+ ];
CGRect selectedViewOutlineFrame = [self frameInLocalCoordinatesForView:self.selectedView];
self.selectedViewOverlay.frame = selectedViewOutlineFrame;
}
}
-- (CGRect)frameInLocalCoordinatesForView:(UIView *)view
-{
- // First convert to window coordinates since the view may be in a different window than our view.
+- (CGRect)frameInLocalCoordinatesForView:(UIView *)view {
+ // Convert to window coordinates since the view may be in a different window than our view
CGRect frameInWindow = [view convertRect:view.bounds toView:nil];
- // Then convert from the window to our view's coordinate space.
+ // Convert from the window to our view's coordinate space
return [self.view convertRect:frameInWindow fromView:nil];
}
#pragma mark - Toolbar Buttons
-- (void)setupToolbarActions
-{
- [self.explorerToolbar.selectItem addTarget:self action:@selector(selectButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
- [self.explorerToolbar.hierarchyItem addTarget:self action:@selector(hierarchyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
- [self.explorerToolbar.moveItem addTarget:self action:@selector(moveButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
- [self.explorerToolbar.globalsItem addTarget:self action:@selector(globalsButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
- [self.explorerToolbar.closeItem addTarget:self action:@selector(closeButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
+- (void)setupToolbarActions {
+ FLEXExplorerToolbar *toolbar = self.explorerToolbar;
+ NSDictionary *actionsToItems = @{
+ NSStringFromSelector(@selector(selectButtonTapped:)): toolbar.selectItem,
+ NSStringFromSelector(@selector(hierarchyButtonTapped:)): toolbar.hierarchyItem,
+ NSStringFromSelector(@selector(recentButtonTapped:)): toolbar.recentItem,
+ NSStringFromSelector(@selector(moveButtonTapped:)): toolbar.moveItem,
+ NSStringFromSelector(@selector(globalsButtonTapped:)): toolbar.globalsItem,
+ NSStringFromSelector(@selector(closeButtonTapped:)): toolbar.closeItem,
+ };
+
+ [actionsToItems enumerateKeysAndObjectsUsingBlock:^(NSString *sel, FLEXExplorerToolbarItem *item, BOOL *stop) {
+ [item addTarget:self action:NSSelectorFromString(sel) forControlEvents:UIControlEventTouchUpInside];
+ }];
}
-- (void)selectButtonTapped:(FLEXToolbarItem *)sender
-{
+- (void)selectButtonTapped:(FLEXExplorerToolbarItem *)sender {
[self toggleSelectTool];
}
-- (void)hierarchyButtonTapped:(FLEXToolbarItem *)sender
-{
+- (void)hierarchyButtonTapped:(FLEXExplorerToolbarItem *)sender {
[self toggleViewsTool];
}
-- (NSArray *)allViewsInHierarchy
-{
- NSMutableArray *allViews = [NSMutableArray array];
- NSArray *windows = [FLEXUtility allWindows];
- for (UIWindow *window in windows) {
- if (window != self.view.window) {
- [allViews addObject:window];
- [allViews addObjectsFromArray:[self allRecursiveSubviewsInView:window]];
- }
- }
- return allViews;
-}
-
-- (UIWindow *)statusWindow
-{
+- (UIWindow *)statusWindow {
NSString *statusBarString = [NSString stringWithFormat:@"%@arWindow", @"_statusB"];
return [UIApplication.sharedApplication valueForKey:statusBarString];
}
-- (void)moveButtonTapped:(FLEXToolbarItem *)sender
-{
+- (void)recentButtonTapped:(FLEXExplorerToolbarItem *)sender {
+ NSAssert(FLEXTabList.sharedList.activeTab, @"Must have active tab");
+ [self presentViewController:FLEXTabList.sharedList.activeTab animated:YES completion:nil];
+}
+
+- (void)moveButtonTapped:(FLEXExplorerToolbarItem *)sender {
[self toggleMoveTool];
}
-- (void)globalsButtonTapped:(FLEXToolbarItem *)sender
-{
+- (void)globalsButtonTapped:(FLEXExplorerToolbarItem *)sender {
[self toggleMenuTool];
}
-- (void)closeButtonTapped:(FLEXToolbarItem *)sender
-{
+- (void)closeButtonTapped:(FLEXExplorerToolbarItem *)sender {
self.currentMode = FLEXExplorerModeDefault;
[self.delegate explorerViewControllerDidFinish:self];
}
-- (void)updateButtonStates
-{
- // Move and details only active when an object is selected.
+- (void)updateButtonStates {
+ FLEXExplorerToolbar *toolbar = self.explorerToolbar;
+
+ toolbar.selectItem.selected = self.currentMode == FLEXExplorerModeSelect;
+
+ // Move only enabled when an object is selected.
BOOL hasSelectedObject = self.selectedView != nil;
- self.explorerToolbar.moveItem.enabled = hasSelectedObject;
- self.explorerToolbar.selectItem.selected = self.currentMode == FLEXExplorerModeSelect;
- self.explorerToolbar.moveItem.selected = self.currentMode == FLEXExplorerModeMove;
+ toolbar.moveItem.enabled = hasSelectedObject;
+ toolbar.moveItem.selected = self.currentMode == FLEXExplorerModeMove;
+
+ // Recent only enabled when we have a last active tab
+ toolbar.recentItem.enabled = FLEXTabList.sharedList.activeTab != nil;
}
#pragma mark - Toolbar Dragging
-- (void)setupToolbarGestures
-{
+- (void)setupToolbarGestures {
+ FLEXExplorerToolbar *toolbar = self.explorerToolbar;
+
// Pan gesture for dragging.
- UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleToolbarPanGesture:)];
- [self.explorerToolbar.dragHandle addGestureRecognizer:panGR];
+ [toolbar.dragHandle addGestureRecognizer:[[UIPanGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleToolbarPanGesture:)
+ ]];
// Tap gesture for hinting.
- UITapGestureRecognizer *hintTapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleToolbarHintTapGesture:)];
- [self.explorerToolbar.dragHandle addGestureRecognizer:hintTapGR];
+ [toolbar.dragHandle addGestureRecognizer:[[UITapGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleToolbarHintTapGesture:)
+ ]];
// Tap gesture for showing additional details
- self.detailsTapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleToolbarDetailsTapGesture:)];
- [self.explorerToolbar.selectedViewDescriptionContainer addGestureRecognizer:self.detailsTapGR];
+ self.detailsTapGR = [[UITapGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleToolbarDetailsTapGesture:)
+ ];
+ [toolbar.selectedViewDescriptionContainer addGestureRecognizer:self.detailsTapGR];
+ // Swipe gestures for selecting deeper / higher views at a point
+ UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleChangeViewAtPointGesture:)
+ ];
+ UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleChangeViewAtPointGesture:)
+ ];
+ leftSwipe.direction = UISwipeGestureRecognizerDirectionLeft;
+ rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
+ [toolbar.selectedViewDescriptionContainer addGestureRecognizer:leftSwipe];
+ [toolbar.selectedViewDescriptionContainer addGestureRecognizer:rightSwipe];
+
+ // Long press gesture to present tabs manager
+ [toolbar.globalsItem addGestureRecognizer:[[UILongPressGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleToolbarShowTabsGesture:)
+ ]];
+
+ // Long press gesture to present window manager
+ [toolbar.selectItem addGestureRecognizer:[[UILongPressGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleToolbarWindowManagerGesture:)
+ ]];
+
+ // Long press gesture to present view controllers at tap
+ [toolbar.hierarchyItem addGestureRecognizer:[[UILongPressGestureRecognizer alloc]
+ initWithTarget:self action:@selector(handleToolbarShowViewControllersGesture:)
+ ]];
}
-- (void)handleToolbarPanGesture:(UIPanGestureRecognizer *)panGR
-{
+- (void)handleToolbarPanGesture:(UIPanGestureRecognizer *)panGR {
switch (panGR.state) {
case UIGestureRecognizerStateBegan:
self.toolbarFrameBeforeDragging = self.explorerToolbar.frame;
@@ -455,8 +494,7 @@ - (void)handleToolbarPanGesture:(UIPanGestureRecognizer *)panGR
}
}
-- (void)updateToolbarPositionWithDragGesture:(UIPanGestureRecognizer *)panGR
-{
+- (void)updateToolbarPositionWithDragGesture:(UIPanGestureRecognizer *)panGR {
CGPoint translation = [panGR translationInView:self.view];
CGRect newToolbarFrame = self.toolbarFrameBeforeDragging;
newToolbarFrame.origin.y += translation.y;
@@ -464,10 +502,10 @@ - (void)updateToolbarPositionWithDragGesture:(UIPanGestureRecognizer *)panGR
[self updateToolbarPositionWithUnconstrainedFrame:newToolbarFrame];
}
-- (void)updateToolbarPositionWithUnconstrainedFrame:(CGRect)unconstrainedFrame
-{
+- (void)updateToolbarPositionWithUnconstrainedFrame:(CGRect)unconstrainedFrame {
CGRect safeArea = [self viewSafeArea];
- // We only constrain the Y-axis because We want the toolbar to handle the X-axis safeArea layout by itself
+ // We only constrain the Y-axis because we want the toolbar
+ // to handle the X-axis safeArea layout by itself
CGFloat minY = CGRectGetMinY(safeArea);
CGFloat maxY = CGRectGetMaxY(safeArea) - unconstrainedFrame.size.height;
if (unconstrainedFrame.origin.y < minY) {
@@ -477,12 +515,10 @@ - (void)updateToolbarPositionWithUnconstrainedFrame:(CGRect)unconstrainedFrame
}
self.explorerToolbar.frame = unconstrainedFrame;
-
- [[NSUserDefaults standardUserDefaults] setDouble:unconstrainedFrame.origin.y forKey:kFLEXToolbarTopMarginDefaultsKey];
+ NSUserDefaults.standardUserDefaults.flex_toolbarTopMargin = unconstrainedFrame.origin.y;
}
-- (void)handleToolbarHintTapGesture:(UITapGestureRecognizer *)tapGR
-{
+- (void)handleToolbarHintTapGesture:(UITapGestureRecognizer *)tapGR {
// Bounce the toolbar to indicate that it is draggable.
// TODO: make it bouncier.
if (tapGR.state == UIGestureRecognizerStateRecognized) {
@@ -501,24 +537,60 @@ - (void)handleToolbarHintTapGesture:(UITapGestureRecognizer *)tapGR
}
}
-- (void)handleToolbarDetailsTapGesture:(UITapGestureRecognizer *)tapGR
-{
+- (void)handleToolbarDetailsTapGesture:(UITapGestureRecognizer *)tapGR {
if (tapGR.state == UIGestureRecognizerStateRecognized && self.selectedView) {
- FLEXObjectExplorerViewController *selectedViewExplorer = [FLEXObjectExplorerFactory explorerViewControllerForObject:self.selectedView];
- selectedViewExplorer.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(selectedViewExplorerFinished:)];
- UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:selectedViewExplorer];
- [self makeKeyAndPresentViewController:navigationController animated:YES completion:nil];
+ UIViewController *topStackVC = [FLEXObjectExplorerFactory explorerViewControllerForObject:self.selectedView];
+ [self presentViewController:
+ [FLEXNavigationController withRootViewController:topStackVC]
+ animated:YES completion:nil];
+ }
+}
+
+- (void)handleToolbarShowTabsGesture:(UILongPressGestureRecognizer *)sender {
+ if (sender.state == UIGestureRecognizerStateBegan) {
+ // Back up the UIMenuController items since dismissViewController: will attempt to replace them
+ self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
+
+ // Don't use FLEXNavigationController because the tab viewer itself is not a tab
+ [super presentViewController:[[UINavigationController alloc]
+ initWithRootViewController:[FLEXTabsViewController new]
+ ] animated:YES completion:nil];
+ }
+}
+
+- (void)handleToolbarWindowManagerGesture:(UILongPressGestureRecognizer *)sender {
+ if (sender.state == UIGestureRecognizerStateBegan) {
+ // Back up the UIMenuController items since dismissViewController: will attempt to replace them
+ self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
+
+ [super presentViewController:[FLEXNavigationController
+ withRootViewController:[FLEXWindowManagerController new]
+ ] animated:YES completion:nil];
+ }
+}
+
+- (void)handleToolbarShowViewControllersGesture:(UILongPressGestureRecognizer *)sender {
+ if (sender.state == UIGestureRecognizerStateBegan && self.viewsAtTapPoint.count) {
+ // Back up the UIMenuController items since dismissViewController: will attempt to replace them
+ self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
+
+ UIViewController *list = [FLEXViewControllersViewController
+ controllersForViews:self.viewsAtTapPoint
+ ];
+ [self presentViewController:
+ [FLEXNavigationController withRootViewController:list
+ ] animated:YES completion:nil];
}
}
#pragma mark - View Selection
-- (void)handleSelectionTap:(UITapGestureRecognizer *)tapGR
-{
+- (void)handleSelectionTap:(UITapGestureRecognizer *)tapGR {
// Only if we're in selection mode
if (self.currentMode == FLEXExplorerModeSelect && tapGR.state == UIGestureRecognizerStateRecognized) {
- // Note that [tapGR locationInView:nil] is broken in iOS 8, so we have to do a two step conversion to window coordinates.
+ // Note that [tapGR locationInView:nil] is broken in iOS 8,
+ // so we have to do a two step conversion to window coordinates.
// Thanks to @lascorbe for finding this: https://github.com/Flipboard/FLEX/pull/31
CGPoint tapPointInView = [tapGR locationInView:self.view];
CGPoint tapPointInWindow = [self.view convertPoint:tapPointInView toView:nil];
@@ -526,8 +598,23 @@ - (void)handleSelectionTap:(UITapGestureRecognizer *)tapGR
}
}
-- (void)updateOutlineViewsForSelectionPoint:(CGPoint)selectionPointInWindow
-{
+- (void)handleChangeViewAtPointGesture:(UISwipeGestureRecognizer *)sender {
+ NSInteger max = self.viewsAtTapPoint.count - 1;
+ NSInteger currentIdx = [self.viewsAtTapPoint indexOfObject:self.selectedView];
+ switch (sender.direction) {
+ case UISwipeGestureRecognizerDirectionLeft:
+ self.selectedView = self.viewsAtTapPoint[MIN(max, currentIdx + 1)];
+ break;
+ case UISwipeGestureRecognizerDirectionRight:
+ self.selectedView = self.viewsAtTapPoint[MAX(0, currentIdx - 1)];
+ break;
+
+ default:
+ break;
+ }
+}
+
+- (void)updateOutlineViewsForSelectionPoint:(CGPoint)selectionPointInWindow {
[self removeAndClearOutlineViews];
// Include hidden views in the "viewsAtTapPoint" array so we can show them in the hierarchy list.
@@ -536,7 +623,7 @@ - (void)updateOutlineViewsForSelectionPoint:(CGPoint)selectionPointInWindow
// For outlined views and the selected view, only use visible views.
// Outlining hidden views adds clutter and makes the selection behavior confusing.
NSArray *visibleViewsAtTapPoint = [self viewsAtPoint:selectionPointInWindow skipHiddenViews:YES];
- NSMutableDictionary *newOutlineViewsForVisibleViews = [NSMutableDictionary dictionary];
+ NSMutableDictionary *newOutlineViewsForVisibleViews = [NSMutableDictionary new];
for (UIView *view in visibleViewsAtTapPoint) {
UIView *outlineView = [self outlineViewForView:view];
[self.view addSubview:outlineView];
@@ -552,8 +639,7 @@ - (void)updateOutlineViewsForSelectionPoint:(CGPoint)selectionPointInWindow
[self updateButtonStates];
}
-- (UIView *)outlineViewForView:(UIView *)view
-{
+- (UIView *)outlineViewForView:(UIView *)view {
CGRect outlineFrame = [self frameInLocalCoordinatesForView:view];
UIView *outlineView = [[UIView alloc] initWithFrame:outlineFrame];
outlineView.backgroundColor = UIColor.clearColor;
@@ -562,8 +648,7 @@ - (UIView *)outlineViewForView:(UIView *)view
return outlineView;
}
-- (void)removeAndClearOutlineViews
-{
+- (void)removeAndClearOutlineViews {
for (NSValue *key in self.outlineViewsForVisibleViews) {
UIView *outlineView = self.outlineViewsForVisibleViews[key];
[outlineView removeFromSuperview];
@@ -571,25 +656,26 @@ - (void)removeAndClearOutlineViews
self.outlineViewsForVisibleViews = nil;
}
-- (NSArray *)viewsAtPoint:(CGPoint)tapPointInWindow skipHiddenViews:(BOOL)skipHidden
-{
- NSMutableArray *views = [NSMutableArray array];
- for (UIWindow *window in [FLEXUtility allWindows]) {
+- (NSArray *)viewsAtPoint:(CGPoint)tapPointInWindow skipHiddenViews:(BOOL)skipHidden {
+ NSMutableArray *views = [NSMutableArray new];
+ for (UIWindow *window in FLEXUtility.allWindows) {
// Don't include the explorer's own window or subviews.
if (window != self.view.window && [window pointInside:tapPointInWindow withEvent:nil]) {
[views addObject:window];
- [views addObjectsFromArray:[self recursiveSubviewsAtPoint:tapPointInWindow inView:window skipHiddenViews:skipHidden]];
+ [views addObjectsFromArray:[self
+ recursiveSubviewsAtPoint:tapPointInWindow inView:window skipHiddenViews:skipHidden
+ ]];
}
}
return views;
}
-- (UIView *)viewForSelectionAtPoint:(CGPoint)tapPointInWindow
-{
- // Select in the window that would handle the touch, but don't just use the result of hitTest:withEvent: so we can still select views with interaction disabled.
+- (UIView *)viewForSelectionAtPoint:(CGPoint)tapPointInWindow {
+ // Select in the window that would handle the touch, but don't just use the result of
+ // hitTest:withEvent: so we can still select views with interaction disabled.
// Default to the the application's key window if none of the windows want the touch.
- UIWindow *windowForSelection = [UIApplication.sharedApplication keyWindow];
- for (UIWindow *window in [FLEXUtility allWindows].reverseObjectEnumerator) {
+ UIWindow *windowForSelection = UIApplication.sharedApplication.keyWindow;
+ for (UIWindow *window in FLEXUtility.allWindows.reverseObjectEnumerator) {
// Ignore the explorer's own window.
if (window != self.view.window) {
if ([window hitTest:tapPointInWindow withEvent:nil]) {
@@ -603,9 +689,10 @@ - (UIView *)viewForSelectionAtPoint:(CGPoint)tapPointInWindow
return [self recursiveSubviewsAtPoint:tapPointInWindow inView:windowForSelection skipHiddenViews:YES].lastObject;
}
-- (NSArray *)recursiveSubviewsAtPoint:(CGPoint)pointInView inView:(UIView *)view skipHiddenViews:(BOOL)skipHidden
-{
- NSMutableArray *subviewsAtPoint = [NSMutableArray array];
+- (NSArray *)recursiveSubviewsAtPoint:(CGPoint)pointInView
+ inView:(UIView *)view
+ skipHiddenViews:(BOOL)skipHidden {
+ NSMutableArray *subviewsAtPoint = [NSMutableArray new];
for (UIView *subview in view.subviews) {
BOOL isHidden = subview.hidden || subview.alpha < 0.01;
if (skipHidden && isHidden) {
@@ -617,46 +704,22 @@ - (UIView *)viewForSelectionAtPoint:(CGPoint)tapPointInWindow
[subviewsAtPoint addObject:subview];
}
- // If this view doesn't clip to its bounds, we need to check its subviews even if it doesn't contain the selection point.
- // They may be visible and contain the selection point.
+ // If this view doesn't clip to its bounds, we need to check its subviews even if it
+ // doesn't contain the selection point. They may be visible and contain the selection point.
if (subviewContainsPoint || !subview.clipsToBounds) {
CGPoint pointInSubview = [view convertPoint:pointInView toView:subview];
- [subviewsAtPoint addObjectsFromArray:[self recursiveSubviewsAtPoint:pointInSubview inView:subview skipHiddenViews:skipHidden]];
+ [subviewsAtPoint addObjectsFromArray:[self
+ recursiveSubviewsAtPoint:pointInSubview inView:subview skipHiddenViews:skipHidden
+ ]];
}
}
return subviewsAtPoint;
}
-- (NSArray *)allRecursiveSubviewsInView:(UIView *)view
-{
- NSMutableArray *subviews = [NSMutableArray array];
- for (UIView *subview in view.subviews) {
- [subviews addObject:subview];
- [subviews addObjectsFromArray:[self allRecursiveSubviewsInView:subview]];
- }
- return subviews;
-}
-
-- (NSDictionary *)hierarchyDepthsForViews:(NSArray *)views
-{
- NSMutableDictionary *hierarchyDepths = [NSMutableDictionary dictionary];
- for (UIView *view in views) {
- NSInteger depth = 0;
- UIView *tryView = view;
- while (tryView.superview) {
- tryView = tryView.superview;
- depth++;
- }
- [hierarchyDepths setObject:@(depth) forKey:[NSValue valueWithNonretainedObject:view]];
- }
- return hierarchyDepths;
-}
-
#pragma mark - Selected View Moving
-- (void)handleMovePan:(UIPanGestureRecognizer *)movePanGR
-{
+- (void)handleMovePan:(UIPanGestureRecognizer *)movePanGR {
switch (movePanGR.state) {
case UIGestureRecognizerStateBegan:
self.selectedViewFrameBeforeDragging = self.selectedView.frame;
@@ -673,8 +736,7 @@ - (void)handleMovePan:(UIPanGestureRecognizer *)movePanGR
}
}
-- (void)updateSelectedViewPositionWithDragGesture:(UIPanGestureRecognizer *)movePanGR
-{
+- (void)updateSelectedViewPositionWithDragGesture:(UIPanGestureRecognizer *)movePanGR {
CGPoint translation = [movePanGR translationInView:self.selectedView.superview];
CGRect newSelectedViewFrame = self.selectedViewFrameBeforeDragging;
newSelectedViewFrame.origin.x = FLEXFloor(newSelectedViewFrame.origin.x + translation.x);
@@ -685,8 +747,7 @@ - (void)updateSelectedViewPositionWithDragGesture:(UIPanGestureRecognizer *)move
#pragma mark - Safe Area Handling
-- (CGRect)viewSafeArea
-{
+- (CGRect)viewSafeArea {
CGRect safeArea = self.view.bounds;
if (@available(iOS 11.0, *)) {
safeArea = UIEdgeInsetsInsetRect(self.view.bounds, self.view.safeAreaInsets);
@@ -695,22 +756,27 @@ - (CGRect)viewSafeArea
return safeArea;
}
-- (void)viewSafeAreaInsetsDidChange
-{
+- (void)viewSafeAreaInsetsDidChange {
if (@available(iOS 11.0, *)) {
[super viewSafeAreaInsetsDidChange];
CGRect safeArea = [self viewSafeArea];
- CGSize toolbarSize = [self.explorerToolbar sizeThatFits:CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(safeArea))];
- [self updateToolbarPositionWithUnconstrainedFrame:CGRectMake(CGRectGetMinX(self.explorerToolbar.frame), CGRectGetMinY(self.explorerToolbar.frame), toolbarSize.width, toolbarSize.height)];
+ CGSize toolbarSize = [self.explorerToolbar sizeThatFits:CGSizeMake(
+ CGRectGetWidth(self.view.bounds), CGRectGetHeight(safeArea)
+ )];
+ [self updateToolbarPositionWithUnconstrainedFrame:CGRectMake(
+ CGRectGetMinX(self.explorerToolbar.frame),
+ CGRectGetMinY(self.explorerToolbar.frame),
+ toolbarSize.width,
+ toolbarSize.height)
+ ];
}
}
#pragma mark - Touch Handling
-- (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates
-{
+- (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates {
BOOL shouldReceiveTouch = NO;
CGPoint pointInLocalCoordinates = [self.view convertPoint:pointInWindowCoordinates fromView:nil];
@@ -739,12 +805,11 @@ - (BOOL)shouldReceiveTouchAtWindowPoint:(CGPoint)pointInWindowCoordinates
}
-#pragma mark - FLEXHierarchyTableViewControllerDelegate
+#pragma mark - FLEXHierarchyDelegate
-- (void)hierarchyViewController:(FLEXHierarchyTableViewController *)hierarchyViewController didFinishWithSelectedView:(UIView *)selectedView
-{
- // Note that we need to wait until the view controller is dismissed to calculated the frame of the outline view.
- // Otherwise the coordinate conversion doesn't give the correct result.
+- (void)viewHierarchyDidDismiss:(UIView *)selectedView {
+ // Note that we need to wait until the view controller is dismissed to calculate the frame
+ // of the outline view, otherwise the coordinate conversion doesn't give the correct result.
[self toggleViewsToolWithCompletion:^{
// If the selected view is outside of the tap point array (selected from "Full Hierarchy"),
// then clear out the tap point array and remove all the outline views.
@@ -764,71 +829,71 @@ - (void)hierarchyViewController:(FLEXHierarchyTableViewController *)hierarchyVie
}
-#pragma mark - FLEXGlobalsViewControllerDelegate
-
-- (void)globalsViewControllerDidFinish:(FLEXGlobalsTableViewController *)globalsViewController
-{
- [self resignKeyAndDismissViewControllerAnimated:YES completion:nil];
-}
-
-
-#pragma mark - FLEXObjectExplorerViewController Done Action
-
-- (void)selectedViewExplorerFinished:(id)sender
-{
- [self resignKeyAndDismissViewControllerAnimated:YES completion:nil];
-}
-
-
#pragma mark - Modal Presentation and Window Management
-- (void)makeKeyAndPresentViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion
-{
- // Save the current key window so we can restore it following dismissal.
- self.previousKeyWindow = [UIApplication.sharedApplication keyWindow];
-
+- (void)presentViewController:(UIViewController *)toPresent
+ animated:(BOOL)animated
+ completion:(void (^)(void))completion {
// Make our window key to correctly handle input.
[self.view.window makeKeyWindow];
-
+
// Move the status bar on top of FLEX so we can get scroll to top behavior for taps.
- [[self statusWindow] setWindowLevel:self.view.window.windowLevel + 1.0];
+ if (!@available(iOS 13, *)) {
+ [self statusWindow].windowLevel = self.view.window.windowLevel + 1.0;
+ }
+
+ // Back up and replace the UIMenuController items
+ // Edit: no longer replacing the items, but still backing them
+ // up in case we start replacing them again in the future
+ self.appMenuItems = UIMenuController.sharedMenuController.menuItems;
- // Show the view controller.
- [self presentViewController:viewController animated:animated completion:completion];
+ // Show the view controller
+ [super presentViewController:toPresent animated:animated completion:completion];
}
-- (void)resignKeyAndDismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion
-{
- UIWindow *previousKeyWindow = self.previousKeyWindow;
- self.previousKeyWindow = nil;
- [previousKeyWindow makeKeyWindow];
- [[previousKeyWindow rootViewController] setNeedsStatusBarAppearanceUpdate];
+- (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion {
+ UIWindow *appWindow = self.window.previousKeyWindow;
+ [appWindow makeKeyWindow];
+ [appWindow.rootViewController setNeedsStatusBarAppearanceUpdate];
+
+ // Restore previous UIMenuController items
+ // Back up and replace the UIMenuController items
+ UIMenuController.sharedMenuController.menuItems = self.appMenuItems;
+ [UIMenuController.sharedMenuController update];
+ self.appMenuItems = nil;
// Restore the status bar window's normal window level.
- // We want it above FLEX while a modal is presented for scroll to top, but below FLEX otherwise for exploration.
- [[self statusWindow] setWindowLevel:UIWindowLevelStatusBar];
+ // We want it above FLEX while a modal is presented for
+ // scroll to top, but below FLEX otherwise for exploration.
+ [self statusWindow].windowLevel = UIWindowLevelStatusBar;
- [self dismissViewControllerAnimated:animated completion:completion];
+ [self updateButtonStates];
+
+ [super dismissViewControllerAnimated:animated completion:completion];
}
- (BOOL)wantsWindowToBecomeKey
{
- return self.previousKeyWindow != nil;
+ return self.window.previousKeyWindow != nil;
}
-- (void)toggleToolWithViewControllerProvider:(UIViewController *(^)(void))future completion:(void(^)(void))completion
-{
+- (void)toggleToolWithViewControllerProvider:(UINavigationController *(^)(void))future
+ completion:(void(^)(void))completion {
if (self.presentedViewController) {
- [self resignKeyAndDismissViewControllerAnimated:YES completion:completion];
+ [self dismissViewControllerAnimated:YES completion:completion];
} else if (future) {
- [self makeKeyAndPresentViewController:future() animated:YES completion:completion];
+ [self presentViewController:future() animated:YES completion:completion];
}
}
+- (FLEXWindow *)window {
+ return (id)self.view.window;
+}
+
+
#pragma mark - Keyboard Shortcut Helpers
-- (void)toggleSelectTool
-{
+- (void)toggleSelectTool {
if (self.currentMode == FLEXExplorerModeSelect) {
self.currentMode = FLEXExplorerModeDefault;
} else {
@@ -836,28 +901,29 @@ - (void)toggleSelectTool
}
}
-- (void)toggleMoveTool
-{
+- (void)toggleMoveTool {
if (self.currentMode == FLEXExplorerModeMove) {
- self.currentMode = FLEXExplorerModeDefault;
- } else {
+ self.currentMode = FLEXExplorerModeSelect;
+ } else if (self.currentMode == FLEXExplorerModeSelect && self.selectedView) {
self.currentMode = FLEXExplorerModeMove;
}
}
-- (void)toggleViewsTool
-{
+- (void)toggleViewsTool {
[self toggleViewsToolWithCompletion:nil];
}
-- (void)toggleViewsToolWithCompletion:(void(^)(void))completion
-{
- [self toggleToolWithViewControllerProvider:^UIViewController *{
- NSArray *allViews = [self allViewsInHierarchy];
- NSDictionary *depthsForViews = [self hierarchyDepthsForViews:allViews];
- FLEXHierarchyTableViewController *hierarchyTVC = [[FLEXHierarchyTableViewController alloc] initWithViews:allViews viewsAtTap:self.viewsAtTapPoint selectedView:self.selectedView depths:depthsForViews];
- hierarchyTVC.delegate = self;
- return [[UINavigationController alloc] initWithRootViewController:hierarchyTVC];
+- (void)toggleViewsToolWithCompletion:(void(^)(void))completion {
+ [self toggleToolWithViewControllerProvider:^UINavigationController *{
+ if (self.selectedView) {
+ return [FLEXHierarchyViewController
+ delegate:self
+ viewsAtTap:self.viewsAtTapPoint
+ selectedView:self.selectedView
+ ];
+ } else {
+ return [FLEXHierarchyViewController delegate:self];
+ }
} completion:^{
if (completion) {
completion();
@@ -865,18 +931,13 @@ - (void)toggleViewsToolWithCompletion:(void(^)(void))completion
}];
}
-- (void)toggleMenuTool
-{
- [self toggleToolWithViewControllerProvider:^UIViewController *{
- FLEXGlobalsTableViewController *globalsViewController = [FLEXGlobalsTableViewController new];
- globalsViewController.delegate = self;
- [FLEXGlobalsTableViewController setApplicationWindow:[UIApplication.sharedApplication keyWindow]];
- return [[UINavigationController alloc] initWithRootViewController:globalsViewController];
+- (void)toggleMenuTool {
+ [self toggleToolWithViewControllerProvider:^UINavigationController *{
+ return [FLEXNavigationController withRootViewController:[FLEXGlobalsViewController new]];
} completion:nil];
}
-- (void)handleDownArrowKeyPressed
-{
+- (BOOL)handleDownArrowKeyPressed {
if (self.currentMode == FLEXExplorerModeMove) {
CGRect frame = self.selectedView.frame;
frame.origin.y += 1.0 / UIScreen.mainScreen.scale;
@@ -886,11 +947,14 @@ - (void)handleDownArrowKeyPressed
if (selectedViewIndex > 0) {
self.selectedView = [self.viewsAtTapPoint objectAtIndex:selectedViewIndex - 1];
}
+ } else {
+ return NO;
}
+
+ return YES;
}
-- (void)handleUpArrowKeyPressed
-{
+- (BOOL)handleUpArrowKeyPressed {
if (self.currentMode == FLEXExplorerModeMove) {
CGRect frame = self.selectedView.frame;
frame.origin.y -= 1.0 / UIScreen.mainScreen.scale;
@@ -900,25 +964,33 @@ - (void)handleUpArrowKeyPressed
if (selectedViewIndex < self.viewsAtTapPoint.count - 1) {
self.selectedView = [self.viewsAtTapPoint objectAtIndex:selectedViewIndex + 1];
}
+ } else {
+ return NO;
}
+
+ return YES;
}
-- (void)handleRightArrowKeyPressed
-{
+- (BOOL)handleRightArrowKeyPressed {
if (self.currentMode == FLEXExplorerModeMove) {
CGRect frame = self.selectedView.frame;
frame.origin.x += 1.0 / UIScreen.mainScreen.scale;
self.selectedView.frame = frame;
+ return YES;
}
+
+ return NO;
}
-- (void)handleLeftArrowKeyPressed
-{
+- (BOOL)handleLeftArrowKeyPressed {
if (self.currentMode == FLEXExplorerModeMove) {
CGRect frame = self.selectedView.frame;
frame.origin.x -= 1.0 / UIScreen.mainScreen.scale;
self.selectedView.frame = frame;
+ return YES;
}
+
+ return NO;
}
@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXViewControllersViewController.h b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXViewControllersViewController.h
new file mode 100644
index 00000000..dcc6decc
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXViewControllersViewController.h
@@ -0,0 +1,19 @@
+//
+// FLEXViewControllersViewController.h
+// FLEX
+//
+// Created by Tanner Bennett on 2/13/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXFilteringTableViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FLEXViewControllersViewController : FLEXFilteringTableViewController
+
++ (instancetype)controllersForViews:(NSArray *)views;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXViewControllersViewController.m b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXViewControllersViewController.m
new file mode 100644
index 00000000..41833f6d
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXViewControllersViewController.m
@@ -0,0 +1,79 @@
+//
+// FLEXViewControllersViewController.m
+// FLEX
+//
+// Created by Tanner Bennett on 2/13/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXViewControllersViewController.h"
+#import "FLEXObjectExplorerFactory.h"
+#import "FLEXMutableListSection.h"
+#import "FLEXUtility.h"
+
+@interface FLEXViewControllersViewController ()
+@property (nonatomic, readonly) FLEXMutableListSection *section;
+@property (nonatomic, readonly) NSArray *controllers;
+@end
+
+@implementation FLEXViewControllersViewController
+@dynamic sections, allSections;
+
+#pragma mark - Initialization
+
++ (instancetype)controllersForViews:(NSArray *)views {
+ return [[self alloc] initWithViews:views];
+}
+
+- (id)initWithViews:(NSArray *)views {
+ NSParameterAssert(views.count);
+
+ self = [self initWithStyle:UITableViewStylePlain];
+ if (self) {
+ _controllers = [views flex_mapped:^id(UIView *view, NSUInteger idx) {
+ return [FLEXUtility viewControllerForView:view];
+ }];
+ }
+
+ return self;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.title = @"View Controllers at Tap";
+ self.showsSearchBar = YES;
+ [self disableToolbar];
+}
+
+- (NSArray *)makeSections {
+ _section = [FLEXMutableListSection list:self.controllers
+ cellConfiguration:^(UITableViewCell *cell, UIViewController *controller, NSInteger row) {
+ cell.textLabel.text = [NSString
+ stringWithFormat:@"%@ — %p", NSStringFromClass(controller.class), controller
+ ];
+ cell.detailTextLabel.text = controller.view.description;
+ cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+ cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
+ } filterMatcher:^BOOL(NSString *filterText, UIViewController *controller) {
+ return [NSStringFromClass(controller.class) localizedCaseInsensitiveContainsString:filterText];
+ }];
+
+ self.section.selectionHandler = ^(UIViewController *host, UIViewController *controller) {
+ [host.navigationController pushViewController:
+ [FLEXObjectExplorerFactory explorerViewControllerForObject:controller]
+ animated:YES];
+ };
+
+ self.section.customTitle = @"View Controllers";
+ return @[self.section];
+}
+
+
+#pragma mark - Private
+
+- (void)dismissAnimated {
+ [self dismissViewControllerAnimated:YES completion:nil];
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindow.h b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindow.h
index 8643c630..5e08df3c 100644
--- a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindow.h
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindow.h
@@ -3,22 +3,27 @@
// Flipboard
//
// Created by Ryan Olson on 4/13/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import
-@protocol FLEXWindowEventDelegate;
-
-@interface FLEXWindow : UIWindow
+@protocol FLEXWindowEventDelegate
-@property (nonatomic, weak) id eventDelegate;
+- (BOOL)shouldHandleTouchAtPoint:(CGPoint)pointInWindow;
+- (BOOL)canBecomeKeyWindow;
@end
-@protocol FLEXWindowEventDelegate
+#pragma mark -
+@interface FLEXWindow : UIWindow
-- (BOOL)shouldHandleTouchAtPoint:(CGPoint)pointInWindow;
-- (BOOL)canBecomeKeyWindow;
+@property (nonatomic, weak) id eventDelegate;
+
+/// Tracked so we can restore the key window after dismissing a modal.
+/// We need to become key after modal presentation so we can correctly capture input.
+/// If we're just showing the toolbar, we want the main app's window to remain key
+/// so that we don't interfere with input, status bar, etc.
+@property (nonatomic, readonly) UIWindow *previousKeyWindow;
@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindow.m b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindow.m
index f74c8bdf..e5ea04bb 100644
--- a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindow.m
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindow.m
@@ -3,19 +3,18 @@
// Flipboard
//
// Created by Ryan Olson on 4/13/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import "FLEXWindow.h"
+#import "FLEXUtility.h"
#import
@implementation FLEXWindow
-- (id)initWithFrame:(CGRect)frame
-{
+- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
- self.backgroundColor = UIColor.clearColor;
// Some apps have windows at UIWindowLevelStatusBar + n.
// If we make the window level too high, we block out UIAlertViews.
// There's a balance between staying above the app's windows and staying below alerts.
@@ -25,8 +24,7 @@ - (id)initWithFrame:(CGRect)frame
return self;
}
-- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
-{
+- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
BOOL pointInside = NO;
if ([self.eventDelegate shouldHandleTouchAtPoint:point]) {
pointInside = [super pointInside:point withEvent:event];
@@ -34,18 +32,25 @@ - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
return pointInside;
}
-- (BOOL)shouldAffectStatusBarAppearance
-{
+- (BOOL)shouldAffectStatusBarAppearance {
return [self isKeyWindow];
}
-- (BOOL)canBecomeKeyWindow
-{
+- (BOOL)canBecomeKeyWindow {
return [self.eventDelegate canBecomeKeyWindow];
}
-+ (void)initialize
-{
+- (void)makeKeyWindow {
+ _previousKeyWindow = FLEXUtility.appKeyWindow;
+ [super makeKeyWindow];
+}
+
+- (void)resignKeyWindow {
+ [super resignKeyWindow];
+ _previousKeyWindow = nil;
+}
+
++ (void)initialize {
// This adds a method (superclass override) at runtime which gives us the status bar behavior we want.
// The FLEX window is intended to be an overlay that generally doesn't affect the app underneath.
// Most of the time, we want the app's main window(s) to be in control of status bar behavior.
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindowManagerController.h b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindowManagerController.h
new file mode 100644
index 00000000..a2a87536
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindowManagerController.h
@@ -0,0 +1,17 @@
+//
+// FLEXWindowManagerController.h
+// FLEX
+//
+// Created by Tanner on 2/6/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FLEXWindowManagerController : FLEXTableViewController
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindowManagerController.m b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindowManagerController.m
new file mode 100644
index 00000000..4b6e9bbc
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/FLEXWindowManagerController.m
@@ -0,0 +1,302 @@
+//
+// FLEXWindowManagerController.m
+// FLEX
+//
+// Created by Tanner on 2/6/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXWindowManagerController.h"
+#import "FLEXManager+Private.h"
+#import "FLEXUtility.h"
+#import "FLEXObjectExplorerFactory.h"
+
+@interface FLEXWindowManagerController ()
+@property (nonatomic) UIWindow *keyWindow;
+@property (nonatomic, copy) NSString *keyWindowSubtitle;
+@property (nonatomic, copy) NSArray *windows;
+@property (nonatomic, copy) NSArray *windowSubtitles;
+@property (nonatomic, copy) NSArray *scenes API_AVAILABLE(ios(13));
+@property (nonatomic, copy) NSArray *sceneSubtitles;
+@property (nonatomic, copy) NSArray *sections;
+@end
+
+@implementation FLEXWindowManagerController
+
+#pragma mark - Initialization
+
+- (id)init {
+ return [self initWithStyle:UITableViewStylePlain];
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.title = @"Windows";
+ if (@available(iOS 13, *)) {
+ self.title = @"Windows and Scenes";
+ }
+
+ [self disableToolbar];
+ [self reloadData];
+}
+
+
+#pragma mark - Private
+
+- (void)reloadData {
+ self.keyWindow = UIApplication.sharedApplication.keyWindow;
+ self.windows = UIApplication.sharedApplication.windows;
+ self.keyWindowSubtitle = self.windowSubtitles[[self.windows indexOfObject:self.keyWindow]];
+ self.windowSubtitles = [self.windows flex_mapped:^id(UIWindow *window, NSUInteger idx) {
+ return [NSString stringWithFormat:@"Level: %@ — Root: %@",
+ @(window.windowLevel), window.rootViewController
+ ];
+ }];
+
+ if (@available(iOS 13, *)) {
+ self.scenes = UIApplication.sharedApplication.connectedScenes.allObjects;
+ self.sceneSubtitles = [self.scenes flex_mapped:^id(UIScene *scene, NSUInteger idx) {
+ return [self sceneDescription:scene];
+ }];
+
+ self.sections = @[@[self.keyWindow], self.windows, self.scenes];
+ } else {
+ self.sections = @[@[self.keyWindow], self.windows];
+ }
+
+ [self.tableView reloadData];
+}
+
+- (void)dismissAnimated {
+ [self dismissViewControllerAnimated:YES completion:nil];
+}
+
+- (void)showRevertOrDismissAlert:(void(^)())revertBlock {
+ [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
+ [self reloadData];
+ [self.tableView reloadData];
+
+ UIWindow *highestWindow = UIApplication.sharedApplication.keyWindow;
+ UIWindowLevel maxLevel = 0;
+ for (UIWindow *window in UIApplication.sharedApplication.windows) {
+ if (window.windowLevel > maxLevel) {
+ maxLevel = window.windowLevel;
+ highestWindow = window;
+ }
+ }
+
+ [FLEXAlert makeAlert:^(FLEXAlert *make) {
+ make.title(@"Keep Changes?");
+ make.message(@"If you do not wish to keep these settings, choose 'Revert Changes' below.");
+
+ make.button(@"Keep Changes").destructiveStyle();
+ make.button(@"Keep Changes and Dismiss").destructiveStyle().handler(^(NSArray *strings) {
+ [self dismissAnimated];
+ });
+ make.button(@"Revert Changes").cancelStyle().handler(^(NSArray *strings) {
+ revertBlock();
+ [self reloadData];
+ [self.tableView reloadData];
+ });
+ } showFrom:[FLEXUtility topViewControllerInWindow:highestWindow]];
+}
+
+- (NSString *)sceneDescription:(UIScene *)scene API_AVAILABLE(ios(13)) {
+ NSString *state = [self stringFromSceneState:scene.activationState];
+ NSString *title = scene.title.length ? scene.title : nil;
+ NSString *suffix = nil;
+
+ if ([scene isKindOfClass:[UIWindowScene class]]) {
+ UIWindowScene *windowScene = (id)scene;
+ suffix = FLEXPluralString(windowScene.windows.count, @"windows", @"window");
+ }
+
+ NSMutableString *description = state.mutableCopy;
+ if (title) {
+ [description appendFormat:@" — %@", title];
+ }
+ if (suffix) {
+ [description appendFormat:@" — %@", suffix];
+ }
+
+ return description.copy;
+}
+
+- (NSString *)stringFromSceneState:(UISceneActivationState)state API_AVAILABLE(ios(13)) {
+ switch (state) {
+ case UISceneActivationStateUnattached:
+ return @"Unattached";
+ case UISceneActivationStateForegroundActive:
+ return @"Active";
+ case UISceneActivationStateForegroundInactive:
+ return @"Inactive";
+ case UISceneActivationStateBackground:
+ return @"Backgrounded";
+ }
+
+ return [NSString stringWithFormat:@"Unknown state: %@", @(state)];
+}
+
+
+#pragma mark - Table View Data Source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+ return self.sections.count;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return self.sections[section].count;
+}
+
+- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
+ switch (section) {
+ case 0: return @"Key Window";
+ case 1: return @"Windows";
+ case 2: return @"Connected Scenes";
+ }
+
+ return nil;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath];
+ cell.accessoryType = UITableViewCellAccessoryDetailButton;
+ cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
+
+ UIWindow *window = nil;
+ NSString *subtitle = nil;
+
+ switch (indexPath.section) {
+ case 0:
+ window = self.keyWindow;
+ subtitle = self.keyWindowSubtitle;
+ break;
+ case 1:
+ window = self.windows[indexPath.row];
+ subtitle = self.windowSubtitles[indexPath.row];
+ break;
+ case 2:
+ if (@available(iOS 13, *)) {
+ UIScene *scene = self.scenes[indexPath.row];
+ cell.textLabel.text = scene.description;
+ cell.detailTextLabel.text = self.sceneSubtitles[indexPath.row];
+ return cell;
+ }
+ }
+
+ cell.textLabel.text = window.description;
+ cell.detailTextLabel.text = [NSString
+ stringWithFormat:@"Level: %@ — Root: %@",
+ @((NSInteger)window.windowLevel), window.rootViewController.class
+ ];
+
+ return cell;
+}
+
+
+#pragma mark - Table View Delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ UIWindow *window = nil;
+ NSString *subtitle = nil;
+ FLEXWindow *flex = FLEXManager.sharedManager.explorerWindow;
+
+ id cancelHandler = ^{
+ [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
+ };
+
+ switch (indexPath.section) {
+ case 0:
+ window = self.keyWindow;
+ subtitle = self.keyWindowSubtitle;
+ break;
+ case 1:
+ window = self.windows[indexPath.row];
+ subtitle = self.windowSubtitles[indexPath.row];
+ break;
+ case 2:
+ if (@available(iOS 13, *)) {
+ UIScene *scene = self.scenes[indexPath.row];
+ UIWindowScene *oldScene = flex.windowScene;
+ BOOL isWindowScene = [scene isKindOfClass:[UIWindowScene class]];
+ BOOL isFLEXScene = isWindowScene ? flex.windowScene == scene : NO;
+
+ [FLEXAlert makeAlert:^(FLEXAlert *make) {
+ make.title(NSStringFromClass(scene.class));
+
+ if (isWindowScene) {
+ if (isFLEXScene) {
+ make.message(@"Already the FLEX window scene");
+ }
+
+ make.button(@"Set as FLEX Window Scene")
+ .handler(^(NSArray *strings) {
+ flex.windowScene = (id)scene;
+ [self showRevertOrDismissAlert:^{
+ flex.windowScene = oldScene;
+ }];
+ }).enabled(!isFLEXScene);
+ make.button(@"Cancel").cancelStyle();
+ } else {
+ make.message(@"Not a UIWindowScene");
+ make.button(@"Dismiss").cancelStyle().handler(cancelHandler);
+ }
+ } showFrom:self];
+ }
+ }
+
+ __block UIWindow *targetWindow = nil, *oldKeyWindow = nil;
+ __block UIWindowLevel oldLevel;
+ __block BOOL wasVisible;
+
+ subtitle = [subtitle stringByAppendingString:
+ @"\n\n1) Adjust the FLEX window level relative to this window,\n"
+ "2) adjust this window's level relative to the FLEX window,\n"
+ "3) set this window's level to a specific value, or\n"
+ "4) make this window the key window if it isn't already."
+ ];
+
+ [FLEXAlert makeAlert:^(FLEXAlert *make) {
+ make.title(NSStringFromClass(window.class)).message(subtitle);
+ make.button(@"Adjust FLEX Window Level").handler(^(NSArray *strings) {
+ targetWindow = flex; oldLevel = flex.windowLevel;
+ flex.windowLevel = window.windowLevel + strings.firstObject.integerValue;
+
+ [self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }];
+ });
+ make.button(@"Adjust This Window's Level").handler(^(NSArray *strings) {
+ targetWindow = window; oldLevel = window.windowLevel;
+ window.windowLevel = flex.windowLevel + strings.firstObject.integerValue;
+
+ [self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }];
+ });
+ make.button(@"Set This Window's Level").handler(^(NSArray *strings) {
+ targetWindow = window; oldLevel = window.windowLevel;
+ window.windowLevel = strings.firstObject.integerValue;
+
+ [self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }];
+ });
+ make.button(@"Make Key And Visible").handler(^(NSArray *strings) {
+ oldKeyWindow = UIApplication.sharedApplication.keyWindow;
+ wasVisible = window.hidden;
+ [window makeKeyAndVisible];
+
+ [self showRevertOrDismissAlert:^{
+ window.hidden = wasVisible;
+ [oldKeyWindow makeKeyWindow];
+ }];
+ }).enabled(!window.isKeyWindow && !window.hidden);
+ make.button(@"Cancel").cancelStyle().handler(cancelHandler);
+
+ make.textField(@"+/- window level, i.e. 5 or -10");
+ } showFrom:self];
+}
+
+- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)ip {
+ [self.navigationController pushViewController:
+ [FLEXObjectExplorerFactory explorerViewControllerForObject:self.sections[ip.section][ip.row]]
+ animated:YES];
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabList.h b/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabList.h
new file mode 100644
index 00000000..a0464c15
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabList.h
@@ -0,0 +1,45 @@
+//
+// FLEXTabList.h
+// FLEX
+//
+// Created by Tanner on 2/1/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FLEXTabList : NSObject
+
+@property (nonatomic, readonly, class) FLEXTabList *sharedList;
+
+@property (nonatomic, readonly, nullable) UINavigationController *activeTab;
+@property (nonatomic, readonly) NSArray *openTabs;
+/// Snapshots of each tab when they were last active.
+@property (nonatomic, readonly) NSArray *openTabSnapshots;
+/// \c NSNotFound if no tabs are present.
+/// Setting this property changes the active tab to one of the already open tabs.
+@property (nonatomic) NSInteger activeTabIndex;
+
+/// Adds a new tab and sets the new tab as the active tab.
+- (void)addTab:(UINavigationController *)newTab;
+/// Closes the given tab. If this tab was the active tab,
+/// the most recent tab before that becomes the active tab.
+- (void)closeTab:(UINavigationController *)tab;
+/// Closes a tab at the given index. If this tab was the active tab,
+/// the most recent tab before that becomes the active tab.
+- (void)closeTabAtIndex:(NSInteger)idx;
+/// Closes all of the tabs at the given indexes. If the active tab
+/// is included, the most recent still-open tab becomes the active tab.
+- (void)closeTabsAtIndexes:(NSIndexSet *)indexes;
+/// A shortcut to close the active tab.
+- (void)closeActiveTab;
+/// A shortcut to close \e every tab.
+- (void)closeAllTabs;
+
+- (void)updateSnapshotForActiveTab;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabList.m b/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabList.m
new file mode 100644
index 00000000..e5af93f9
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabList.m
@@ -0,0 +1,133 @@
+//
+// FLEXTabList.m
+// FLEX
+//
+// Created by Tanner on 2/1/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXTabList.h"
+#import "FLEXUtility.h"
+
+@interface FLEXTabList () {
+ NSMutableArray *_openTabs;
+ NSMutableArray *_openTabSnapshots;
+}
+@end
+#pragma mark -
+@implementation FLEXTabList
+
+#pragma mark Initialization
+
++ (FLEXTabList *)sharedList {
+ static FLEXTabList *sharedList = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ sharedList = [self new];
+ });
+
+ return sharedList;
+}
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ _openTabs = [NSMutableArray new];
+ _openTabSnapshots = [NSMutableArray new];
+ _activeTabIndex = NSNotFound;
+ }
+
+ return self;
+}
+
+
+#pragma mark Private
+
+- (void)chooseNewActiveTab {
+ if (self.openTabs.count) {
+ self.activeTabIndex = self.openTabs.count - 1;
+ } else {
+ self.activeTabIndex = NSNotFound;
+ }
+}
+
+
+#pragma mark Public
+
+- (void)setActiveTabIndex:(NSInteger)idx {
+ NSParameterAssert(idx < self.openTabs.count || idx == NSNotFound);
+ if (_activeTabIndex == idx) return;
+
+ _activeTabIndex = idx;
+ _activeTab = (idx == NSNotFound) ? nil : self.openTabs[idx];
+}
+
+- (void)addTab:(UINavigationController *)newTab {
+ NSParameterAssert(newTab);
+
+ // Update snapshot of the last active tab
+ if (self.activeTab) {
+ [self updateSnapshotForActiveTab];
+ }
+
+ // Add new tab and snapshot,
+ // update active tab and index
+ [_openTabs addObject:newTab];
+ [_openTabSnapshots addObject:[FLEXUtility previewImageForView:newTab.view]];
+ _activeTab = newTab;
+ _activeTabIndex = self.openTabs.count - 1;
+}
+
+- (void)closeTab:(UINavigationController *)tab {
+ NSParameterAssert(tab);
+ NSParameterAssert([self.openTabs containsObject:tab]);
+ NSInteger idx = [self.openTabs indexOfObject:tab];
+
+ [self closeTabAtIndex:idx];
+}
+
+- (void)closeTabAtIndex:(NSInteger)idx {
+ NSParameterAssert(idx < self.openTabs.count);
+
+ // Remove old tab and snapshot
+ [_openTabs removeObjectAtIndex:idx];
+ [_openTabSnapshots removeObjectAtIndex:idx];
+
+ // Update active tab and index if needed
+ if (self.activeTabIndex == idx) {
+ [self chooseNewActiveTab];
+ }
+}
+
+- (void)closeTabsAtIndexes:(NSIndexSet *)indexes {
+ // Remove old tabs and snapshot
+ [_openTabs removeObjectsAtIndexes:indexes];
+ [_openTabSnapshots removeObjectsAtIndexes:indexes];
+
+ // Update active tab and index if needed
+ if ([indexes containsIndex:self.activeTabIndex]) {
+ [self chooseNewActiveTab];
+ }
+}
+
+- (void)closeActiveTab {
+ [self closeTab:self.activeTab];
+}
+
+- (void)closeAllTabs {
+ // Remove tabs and snapshots
+ [_openTabs removeAllObjects];
+ [_openTabSnapshots removeAllObjects];
+
+ // Update active tab index
+ self.activeTabIndex = NSNotFound;
+}
+
+- (void)updateSnapshotForActiveTab {
+ if (self.activeTabIndex != NSNotFound) {
+ UIImage *newSnapshot = [FLEXUtility previewImageForView:self.activeTab.view];
+ [_openTabSnapshots replaceObjectAtIndex:self.activeTabIndex withObject:newSnapshot];
+ }
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabsViewController.h b/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabsViewController.h
new file mode 100644
index 00000000..d3d72f6d
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabsViewController.h
@@ -0,0 +1,13 @@
+//
+// FLEXTabsViewController.h
+// FLEX
+//
+// Created by Tanner on 2/4/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXTableViewController.h"
+
+@interface FLEXTabsViewController : FLEXTableViewController
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabsViewController.m b/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabsViewController.m
new file mode 100644
index 00000000..8e2eca47
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/ExplorerInterface/Tabs/FLEXTabsViewController.m
@@ -0,0 +1,335 @@
+//
+// FLEXTabsViewController.m
+// FLEX
+//
+// Created by Tanner on 2/4/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import "FLEXTabsViewController.h"
+#import "FLEXNavigationController.h"
+#import "FLEXTabList.h"
+#import "FLEXBookmarkManager.h"
+#import "FLEXTableView.h"
+#import "FLEXUtility.h"
+#import "FLEXColor.h"
+#import "UIBarButtonItem+FLEX.h"
+#import "FLEXExplorerViewController.h"
+#import "FLEXGlobalsViewController.h"
+#import "FLEXBookmarksViewController.h"
+
+@interface FLEXTabsViewController ()
+@property (nonatomic, copy) NSArray *openTabs;
+@property (nonatomic, copy) NSArray *tabSnapshots;
+@property (nonatomic) NSInteger activeIndex;
+@property (nonatomic) BOOL presentNewActiveTabOnDismiss;
+
+@property (nonatomic, readonly) FLEXExplorerViewController *corePresenter;
+@end
+
+@implementation FLEXTabsViewController
+
+#pragma mark - Initialization
+
+- (id)init {
+ return [self initWithStyle:UITableViewStylePlain];
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.title = @"Open Tabs";
+ self.navigationController.hidesBarsOnSwipe = NO;
+ self.tableView.allowsMultipleSelectionDuringEditing = YES;
+
+ [self reloadData:NO];
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+ [self setupDefaultBarItems];
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+
+ // Instead of updating the active snapshot before we present,
+ // we update it after we present to avoid pre-presenation latency
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [FLEXTabList.sharedList updateSnapshotForActiveTab];
+ [self reloadData:NO];
+ [self.tableView reloadData];
+ });
+}
+
+
+#pragma mark - Private
+
+/// @param trackActiveTabDelta whether to check if the active
+/// tab changed and needs to be presented upon "Done" dismissal.
+/// @return whether the active tab changed or not (if there are any tabs left)
+- (BOOL)reloadData:(BOOL)trackActiveTabDelta {
+ BOOL activeTabDidChange = NO;
+ FLEXTabList *list = FLEXTabList.sharedList;
+
+ // Flag to enable check to determine whether
+ if (trackActiveTabDelta) {
+ NSInteger oldActiveIndex = self.activeIndex;
+ if (oldActiveIndex != list.activeTabIndex && list.activeTabIndex != NSNotFound) {
+ self.presentNewActiveTabOnDismiss = YES;
+ activeTabDidChange = YES;
+ } else if (self.presentNewActiveTabOnDismiss) {
+ // If we had something to present before, now we don't
+ // (i.e. activeTabIndex == NSNotFound)
+ self.presentNewActiveTabOnDismiss = NO;
+ }
+ }
+
+ // We assume the tabs aren't going to change out from under us, since
+ // presenting any other tool via keyboard shortcuts should dismiss us first
+ self.openTabs = list.openTabs;
+ self.tabSnapshots = list.openTabSnapshots;
+ self.activeIndex = list.activeTabIndex;
+
+ return activeTabDidChange;
+}
+
+- (void)reloadActiveTabRowIfChanged:(BOOL)activeTabChanged {
+ // Refresh the newly active tab row if needed
+ if (activeTabChanged) {
+ NSIndexPath *active = [NSIndexPath
+ indexPathForRow:self.activeIndex inSection:0
+ ];
+ [self.tableView reloadRowsAtIndexPaths:@[active] withRowAnimation:UITableViewRowAnimationNone];
+ }
+}
+
+- (void)setupDefaultBarItems {
+ self.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(Done, self, @selector(dismissAnimated));
+ self.toolbarItems = @[
+ UIBarButtonItem.flex_fixedSpace,
+ UIBarButtonItem.flex_flexibleSpace,
+ FLEXBarButtonItemSystem(Add, self, @selector(addTabButtonPressed:)),
+ UIBarButtonItem.flex_flexibleSpace,
+ FLEXBarButtonItemSystem(Edit, self, @selector(toggleEditing)),
+ ];
+
+ // Disable editing if no tabs available
+ self.toolbarItems.lastObject.enabled = self.openTabs.count > 0;
+}
+
+- (void)setupEditingBarItems {
+ self.navigationItem.rightBarButtonItem = nil;
+ self.toolbarItems = @[
+ [UIBarButtonItem itemWithTitle:@"Close All" target:self action:@selector(closeAllButtonPressed:)],
+ UIBarButtonItem.flex_flexibleSpace,
+ [UIBarButtonItem disabledSystemItem:UIBarButtonSystemItemAdd],
+ UIBarButtonItem.flex_flexibleSpace,
+ // We use a non-system done item because we change its title dynamically
+ [UIBarButtonItem doneStyleitemWithTitle:@"Done" target:self action:@selector(toggleEditing)]
+ ];
+
+ self.toolbarItems.firstObject.tintColor = FLEXColor.destructiveColor;
+}
+
+- (FLEXExplorerViewController *)corePresenter {
+ // We must be presented by a FLEXExplorerViewController, or presented
+ // by another view controller that was presented by FLEXExplorerViewController
+ FLEXExplorerViewController *presenter = (id)self.presentingViewController;
+ presenter = (id)presenter.presentingViewController ?: presenter;
+ NSAssert(
+ [presenter isKindOfClass:[FLEXExplorerViewController class]],
+ @"The tabs view controller expects to be presented by the explorer controller"
+ );
+ return presenter;
+}
+
+
+#pragma mark Button Actions
+
+- (void)dismissAnimated {
+ if (self.presentNewActiveTabOnDismiss) {
+ // The active tab was closed so we need to present the new one
+ UIViewController *activeTab = FLEXTabList.sharedList.activeTab;
+ FLEXExplorerViewController *presenter = self.corePresenter;
+ [presenter dismissViewControllerAnimated:YES completion:^{
+ [presenter presentViewController:activeTab animated:YES completion:nil];
+ }];
+ } else if (self.activeIndex == NSNotFound) {
+ // The only tab was closed, so dismiss everything
+ [self.corePresenter dismissViewControllerAnimated:YES completion:nil];
+ } else {
+ // Simple dismiss with the same active tab, only dismiss myself
+ [self dismissViewControllerAnimated:YES completion:nil];
+ }
+}
+
+- (void)toggleEditing {
+ NSArray *selected = self.tableView.indexPathsForSelectedRows;
+ self.editing = !self.editing;
+
+ if (self.isEditing) {
+ [self setupEditingBarItems];
+ } else {
+ [self setupDefaultBarItems];
+
+ // Get index set of tabs to close
+ NSMutableIndexSet *indexes = [NSMutableIndexSet new];
+ for (NSIndexPath *ip in selected) {
+ [indexes addIndex:ip.row];
+ }
+
+ if (selected.count) {
+ // Close tabs and update data source
+ [FLEXTabList.sharedList closeTabsAtIndexes:indexes];
+ BOOL activeTabChanged = [self reloadData:YES];
+
+ // Remove deleted rows
+ [self.tableView deleteRowsAtIndexPaths:selected withRowAnimation:UITableViewRowAnimationAutomatic];
+
+ // Refresh the newly active tab row if needed
+ [self reloadActiveTabRowIfChanged:activeTabChanged];
+ }
+ }
+}
+
+- (void)addTabButtonPressed:(UIBarButtonItem *)sender {
+ if (FLEXBookmarkManager.bookmarks.count) {
+ [FLEXAlert makeSheet:^(FLEXAlert *make) {
+ make.title(@"New Tab");
+ make.button(@"Main Menu").handler(^(NSArray *strings) {
+ [self addTabAndDismiss:[FLEXNavigationController
+ withRootViewController:[FLEXGlobalsViewController new]
+ ]];
+ });
+ make.button(@"Choose from Bookmarks").handler(^(NSArray *strings) {
+ [self presentViewController:[FLEXNavigationController
+ withRootViewController:[FLEXBookmarksViewController new]
+ ] animated:YES completion:nil];
+ });
+ make.button(@"Cancel").cancelStyle();
+ } showFrom:self source:sender];
+ } else {
+ // No bookmarks, just open the main menu
+ [self addTabAndDismiss:[FLEXNavigationController
+ withRootViewController:[FLEXGlobalsViewController new]
+ ]];
+ }
+}
+
+- (void)addTabAndDismiss:(UINavigationController *)newTab {
+ FLEXExplorerViewController *presenter = self.corePresenter;
+ [presenter dismissViewControllerAnimated:YES completion:^{
+ [presenter presentViewController:newTab animated:YES completion:nil];
+ }];
+}
+
+- (void)closeAllButtonPressed:(UIBarButtonItem *)sender {
+ [FLEXAlert makeSheet:^(FLEXAlert *make) {
+ NSInteger count = self.openTabs.count;
+ NSString *title = FLEXPluralFormatString(count, @"Close %@ tabs", @"Close %@ tab");
+ make.button(title).destructiveStyle().handler(^(NSArray *strings) {
+ [self closeAll];
+ [self toggleEditing];
+ });
+ make.button(@"Cancel").cancelStyle();
+ } showFrom:self source:sender];
+}
+
+- (void)closeAll {
+ NSInteger rowCount = self.openTabs.count;
+
+ // Close tabs and update data source
+ [FLEXTabList.sharedList closeAllTabs];
+ [self reloadData:YES];
+
+ // Delete rows from table view
+ NSArray *allRows = [NSArray flex_forEachUpTo:rowCount map:^id(NSUInteger row) {
+ return [NSIndexPath indexPathForRow:row inSection:0];
+ }];
+ [self.tableView deleteRowsAtIndexPaths:allRows withRowAnimation:UITableViewRowAnimationAutomatic];
+}
+
+
+#pragma mark - Table View Data Source
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return self.openTabs.count;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath];
+
+ UINavigationController *tab = self.openTabs[indexPath.row];
+ cell.imageView.image = self.tabSnapshots[indexPath.row];
+ cell.textLabel.text = tab.topViewController.title;
+ cell.detailTextLabel.text = FLEXPluralString(tab.viewControllers.count, @"pages", @"page");
+
+ if (!cell.tag) {
+ cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
+ cell.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
+ cell.detailTextLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
+ cell.tag = 1;
+ }
+
+ if (indexPath.row == self.activeIndex) {
+ cell.backgroundColor = FLEXColor.secondaryBackgroundColor;
+ } else {
+ cell.backgroundColor = FLEXColor.primaryBackgroundColor;
+ }
+
+ return cell;
+}
+
+
+#pragma mark - Table View Delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ if (self.editing) {
+ // Case: editing with multi-select
+ self.toolbarItems.lastObject.title = @"Close Selected";
+ self.toolbarItems.lastObject.tintColor = FLEXColor.destructiveColor;
+ } else {
+ if (self.activeIndex == indexPath.row && self.corePresenter != self.presentingViewController) {
+ // Case: selected the already active tab
+ [self dismissAnimated];
+ } else {
+ // Case: selected a different tab,
+ // or selected a tab when presented from the FLEX toolbar
+ FLEXTabList.sharedList.activeTabIndex = indexPath.row;
+ self.presentNewActiveTabOnDismiss = YES;
+ [self dismissAnimated];
+ }
+ }
+}
+
+- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
+ NSParameterAssert(self.editing);
+
+ if (tableView.indexPathsForSelectedRows.count == 0) {
+ self.toolbarItems.lastObject.title = @"Done";
+ self.toolbarItems.lastObject.tintColor = self.view.tintColor;
+ }
+}
+
+- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
+ return YES;
+}
+
+- (void)tableView:(UITableView *)table
+commitEditingStyle:(UITableViewCellEditingStyle)edit
+forRowAtIndexPath:(NSIndexPath *)indexPath {
+ NSParameterAssert(edit == UITableViewCellEditingStyleDelete);
+
+ // Close tab and update data source
+ [FLEXTabList.sharedList closeTab:self.openTabs[indexPath.row]];
+ BOOL activeTabChanged = [self reloadData:YES];
+
+ // Delete row from table view
+ [table deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
+
+ // Refresh the newly active tab row if needed
+ [self reloadActiveTabRowIfChanged:activeTabChanged];
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/FLEX-Categories.h b/xcode/Pods/FLEX/Classes/FLEX-Categories.h
new file mode 100644
index 00000000..fb9a5fca
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/FLEX-Categories.h
@@ -0,0 +1,27 @@
+//
+// FLEX-Categories.h
+// FLEX
+//
+// Created by Tanner on 3/12/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+
+
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
diff --git a/xcode/Pods/FLEX/Classes/FLEX-Core.h b/xcode/Pods/FLEX/Classes/FLEX-Core.h
new file mode 100644
index 00000000..5f0f11cf
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/FLEX-Core.h
@@ -0,0 +1,23 @@
+//
+// FLEX-Core.h
+// FLEX
+//
+// Created by Tanner on 3/11/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import
+#import
+#import
+#import
+
+#import
+#import
+
+#import
+#import
+#import
+#import
+#import
+
+#import
diff --git a/xcode/Pods/FLEX/Classes/FLEX-ObjectExploring.h b/xcode/Pods/FLEX/Classes/FLEX-ObjectExploring.h
new file mode 100644
index 00000000..808b9bbc
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/FLEX-ObjectExploring.h
@@ -0,0 +1,30 @@
+//
+// FLEX-ObjectExploring.h
+// FLEX
+//
+// Created by Tanner on 3/11/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import
+#import
+
+#import
+
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+
+#import
+#import
+#import
+#import
+#import
+#import
diff --git a/xcode/Pods/FLEX/Classes/FLEX-Runtime.h b/xcode/Pods/FLEX/Classes/FLEX-Runtime.h
new file mode 100644
index 00000000..91e8288f
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/FLEX-Runtime.h
@@ -0,0 +1,25 @@
+//
+// FLEX-Runtime.h
+// FLEX
+//
+// Created by Tanner on 3/11/20.
+// Copyright © 2020 Flipboard. All rights reserved.
+//
+
+#import
+#import
+#import
+#import
+
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+#import
+
+#import
+#import
diff --git a/xcode/Pods/FLEX/Classes/FLEX.h b/xcode/Pods/FLEX/Classes/FLEX.h
index bb6cd2f1..14fba2d9 100644
--- a/xcode/Pods/FLEX/Classes/FLEX.h
+++ b/xcode/Pods/FLEX/Classes/FLEX.h
@@ -3,7 +3,23 @@
// FLEX
//
// Created by Eric Horacek on 7/18/15.
-// Copyright (c) 2015 Flipboard. All rights reserved.
+// Modified by Tanner Bennett on 3/12/20.
+// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import
+#import
+#import
+
+#import
+#import
+#import
+
+#import
+#import
+#import
+#import
+
+#import
+#import
+#import
diff --git a/xcode/Pods/FLEX/Classes/FLEXManager.h b/xcode/Pods/FLEX/Classes/FLEXManager.h
deleted file mode 100644
index 6b520569..00000000
--- a/xcode/Pods/FLEX/Classes/FLEXManager.h
+++ /dev/null
@@ -1,99 +0,0 @@
-//
-// FLEXManager.h
-// Flipboard
-//
-// Created by Ryan Olson on 4/4/14.
-// Copyright (c) 2014 Flipboard. All rights reserved.
-//
-
-#import
-#import
-
-#if !FLEX_AT_LEAST_IOS13_SDK
-@class UIWindowScene;
-#endif
-
-typedef UIViewController *(^FLEXCustomContentViewerFuture)(NSData *data);
-
-@interface FLEXManager : NSObject
-
-+ (instancetype)sharedManager;
-
-@property (nonatomic, readonly) BOOL isHidden;
-
-- (void)showExplorer;
-- (void)hideExplorer;
-- (void)toggleExplorer;
-
-/// Use this to present the explorer in a specific scene when the one
-/// it chooses by default is not the one you wish to display it in.
-- (void)showExplorerFromScene:(UIWindowScene *)scene API_AVAILABLE(ios(13.0));
-
-#pragma mark - Network Debugging
-
-/// If this property is set to YES, FLEX will swizzle NSURLConnection*Delegate and NSURLSession*Delegate methods
-/// on classes that conform to the protocols. This allows you to view network activity history from the main FLEX menu.
-/// Full responses are kept temporarily in a size-limited cache and may be pruned under memory pressure.
-@property (nonatomic, getter=isNetworkDebuggingEnabled) BOOL networkDebuggingEnabled;
-
-/// Defaults to 25 MB if never set. Values set here are persisted across launches of the app.
-/// The response cache uses an NSCache, so it may purge prior to hitting the limit when the app is under memory pressure.
-@property (nonatomic) NSUInteger networkResponseCacheByteLimit;
-
-/// Requests whose host ends with one of the blacklisted entries in this array will be not be recorded (eg. google.com).
-/// Wildcard or subdomain entries are not required (eg. google.com will match any subdomain under google.com).
-/// Useful to remove requests that are typically noisy, such as analytics requests that you aren't interested in tracking.
-@property (nonatomic, copy) NSArray *networkRequestHostBlacklist;
-
-
-#pragma mark - Keyboard Shortcuts
-
-/// Simulator keyboard shortcuts are enabled by default.
-/// The shortcuts will not fire when there is an active text field, text view, or other responder accepting key input.
-/// You can disable keyboard shortcuts if you have existing keyboard shortcuts that conflict with FLEX, or if you like doing things the hard way ;)
-/// Keyboard shortcuts are always disabled (and support is compiled out) in non-simulator builds
-@property (nonatomic) BOOL simulatorShortcutsEnabled;
-
-/// Adds an action to run when the specified key & modifier combination is pressed
-/// @param key A single character string matching a key on the keyboard
-/// @param modifiers Modifier keys such as shift, command, or alt/option
-/// @param action The block to run on the main thread when the key & modifier combination is recognized.
-/// @param description Shown the the keyboard shortcut help menu, which is accessed via the '?' key.
-/// @note The action block will be retained for the duration of the application. You may want to use weak references.
-/// @note FLEX registers several default keyboard shortcuts. Use the '?' key to see a list of shortcuts.
-- (void)registerSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description;
-
-#pragma mark - Extensions
-
-/// Default database password is @c nil by default.
-/// Set this to the password you want the databases to open with.
-@property (copy, nonatomic) NSString *defaultSqliteDatabasePassword;
-
-/// Adds an entry at the bottom of the list of Global State items. Call this method before this view controller is displayed.
-/// @param entryName The string to be displayed in the cell.
-/// @param objectFutureBlock When you tap on the row, information about the object returned by this block will be displayed.
-/// Passing a block that returns an object allows you to display information about an object whose actual pointer may change at runtime (e.g. +currentUser)
-/// @note This method must be called from the main thread.
-/// The objectFutureBlock will be invoked from the main thread and may return nil.
-/// @note The passed block will be copied and retain for the duration of the application, you may want to use __weak references.
-- (void)registerGlobalEntryWithName:(NSString *)entryName objectFutureBlock:(id (^)(void))objectFutureBlock;
-
-/// Adds an entry at the bottom of the list of Global State items. Call this method before this view controller is displayed.
-/// @param entryName The string to be displayed in the cell.
-/// @param viewControllerFutureBlock When you tap on the row, view controller returned by this block will be pushed on the navigation controller stack.
-/// @note This method must be called from the main thread.
-/// The viewControllerFutureBlock will be invoked from the main thread and may not return nil.
-/// @note The passed block will be copied and retain for the duration of the application, you may want to use __weak references.
-- (void)registerGlobalEntryWithName:(NSString *)entryName
- viewControllerFutureBlock:(UIViewController * (^)(void))viewControllerFutureBlock;
-
-/// Sets custom viewer for specific content type.
-/// @param contentType Mime type like application/json
-/// @param viewControllerFutureBlock Viewer (view controller) creation block
-/// @note This method must be called from the main thread.
-/// The viewControllerFutureBlock will be invoked from the main thread and may not return nil.
-/// @note The passed block will be copied and retain for the duration of the application, you may want to use __weak references.
-- (void)setCustomViewerForContentType:(NSString *)contentType
- viewControllerFutureBlock:(FLEXCustomContentViewerFuture)viewControllerFutureBlock;
-
-@end
diff --git a/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDBQueryRowCell.h b/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDBQueryRowCell.h
new file mode 100644
index 00000000..a4791a8c
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDBQueryRowCell.h
@@ -0,0 +1,19 @@
+//
+// FLEXDBQueryRowCell.h
+// FLEX
+//
+// Created by Peng Tao on 15/11/24.
+// Copyright © 2015年 f. All rights reserved.
+//
+
+#import
+
+extern NSString * const kFLEXDBQueryRowCellReuse;
+
+
+@interface FLEXDBQueryRowCell : UITableViewCell
+
+/// An array of NSString, NSNumber, or NSData objects
+@property (nonatomic) NSArray *data;
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDBQueryRowCell.m b/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDBQueryRowCell.m
new file mode 100644
index 00000000..ce6ff581
--- /dev/null
+++ b/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDBQueryRowCell.m
@@ -0,0 +1,74 @@
+//
+// FLEXDBQueryRowCell.m
+// FLEX
+//
+// Created by Peng Tao on 15/11/24.
+// Copyright © 2015年 f. All rights reserved.
+//
+
+#import "FLEXDBQueryRowCell.h"
+#import "FLEXMultiColumnTableView.h"
+#import "NSArray+FLEX.h"
+#import "UIFont+FLEX.h"
+#import "FLEXColor.h"
+
+NSString * const kFLEXDBQueryRowCellReuse = @"kFLEXDBQueryRowCellReuse";
+
+@interface FLEXDBQueryRowCell ()
+@property (nonatomic) NSInteger columnCount;
+@property (nonatomic) NSArray *labels;
+@end
+
+@implementation FLEXDBQueryRowCell
+
+- (void)setData:(NSArray *)data {
+ _data = data;
+ self.columnCount = data.count;
+
+ [self.labels flex_forEach:^(UILabel *label, NSUInteger idx) {
+ id content = self.data[idx];
+
+ if ([content isKindOfClass:[NSString class]]) {
+ label.text = content;
+ } else if (content == NSNull.null) {
+ label.text = @"";
+ label.textColor = FLEXColor.deemphasizedTextColor;
+ } else {
+ label.text = [content description];
+ }
+ }];
+}
+
+- (void)setColumnCount:(NSInteger)columnCount {
+ if (columnCount != _columnCount) {
+ _columnCount = columnCount;
+
+ // Remove existing labels
+ for (UILabel *l in self.labels) {
+ [l removeFromSuperview];
+ }
+
+ // Create new labels
+ self.labels = [NSArray flex_forEachUpTo:columnCount map:^id(NSUInteger i) {
+ UILabel *label = [UILabel new];
+ label.font = UIFont.flex_defaultTableCellFont;
+ label.textAlignment = NSTextAlignmentLeft;
+ [self.contentView addSubview:label];
+
+ return label;
+ }];
+ }
+}
+
+- (void)layoutSubviews {
+ [super layoutSubviews];
+
+ CGFloat width = self.contentView.frame.size.width / self.labels.count;
+ CGFloat height = self.contentView.frame.size.height;
+
+ [self.labels flex_forEach:^(UILabel *label, NSUInteger i) {
+ label.frame = CGRectMake(width * i + 5, 0, (width - 10), height);
+ }];
+}
+
+@end
diff --git a/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h b/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h
index 19312a07..a28d0ced 100644
--- a/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h
+++ b/xcode/Pods/FLEX/Classes/GlobalStateExplorers/DatabaseBrowser/FLEXDatabaseManager.h
@@ -12,15 +12,23 @@
// which Flying Meat Inc. licenses this file to you.
#import
+#import "FLEXSQLResult.h"
+/// Conformers should automatically open and close the database
@protocol FLEXDatabaseManager
@required
-- (instancetype)initWithPath:(NSString*)path;
-- (BOOL)open;
-- (NSArray