├── .gitignore ├── Configs ├── RxModal.plist └── RxModalTests.plist ├── LICENSE ├── Package.swift ├── README.md ├── RxModal.podspec ├── RxModal.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── RxModal-iOS.xcscheme │ ├── RxModal-macOS.xcscheme │ ├── RxModal-tvOS.xcscheme │ └── RxModal-watchOS.xcscheme ├── RxModalExample ├── Podfile ├── Podfile.lock ├── RxModalExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── RxModalExample.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── swiftpm │ │ └── Package.resolved └── RxModalExample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon.png │ │ ├── icon_128pt.png │ │ ├── icon_256pt-1.png │ │ ├── icon_256pt.png │ │ ├── icon_512pt-1.png │ │ ├── icon_512pt.png │ │ ├── icon_512pt@2x.png │ │ ├── icon_60pt@2x.png │ │ ├── icon_60pt@3x.png │ │ ├── icon_76pt.png │ │ ├── icon_76pt@2x.png │ │ └── icon_83.5@2x.png │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Flow.swift │ ├── FlowDetailViewController.swift │ ├── FlowsListViewController.swift │ ├── Info.plist │ ├── RxModalExample.entitlements │ └── SceneDelegate.swift ├── Sources ├── Composers │ ├── MFMailComposeViewController.swift │ └── MFMessageComposeViewController.swift ├── Dialogs │ ├── Dialog.swift │ └── UIAlertController.swift ├── Internals.swift ├── Other │ └── ASWebAuthenticationSession.swift ├── Pickers │ ├── MPMediaPickerController.swift │ └── PHPickerViewController.swift ├── Presenter.swift ├── Rx+AuthorizationStatus.swift ├── RxModal.swift ├── RxModalCoordinator.swift └── RxModalDescription.swift ├── Tests ├── LinuxMain.swift └── RxModalTests │ └── RxModalTests.swift └── assets ├── RxModal_Demo.gif └── RxModal_Icons.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | 48 | Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /Configs/RxModal.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2021 Jérôme Alves. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Configs/RxModalTests.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Jérôme Alves 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "RxModal", 8 | products: [ 9 | .library(name: "RxModal", targets: ["RxModal"]), 10 | ], 11 | dependencies: [ 12 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.0.0")), 13 | ], 14 | targets: [ 15 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 16 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 17 | .target( 18 | name: "RxModal", 19 | dependencies: ["RxSwift", "RxCocoa"], 20 | path: "Sources" 21 | ), 22 | .testTarget( 23 | name: "RxModalTests", 24 | dependencies: ["RxModal"] 25 | ), 26 | ] 27 | ) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | RxModal Icons 3 |
4 | 5 | 6 |

7 | 8 | # RxModal 9 | 10 | **RxModal** enforces the simple idea that a modal flow can be considered as a simple asynchroneous event: 11 | - the view controller is presented on subscribe 12 | - the user do what they want to do in the modal view 13 | - the view controller is dismissed on dispose and eventually emit a value or an error 14 | 15 | # Usage 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 58 | 61 | 62 |
Here's an exampleIn Action
 24 |   let mailComposer = RxModal.mailComposer {
 25 |       $0.setToRecipients([
 26 |           "rxmodal@rxswiftcommunity.org"
 27 |       ])
 28 |       $0.setMessageBody(
 29 |           "Hello World!",
 30 |           isHTML: false
 31 |       )
 32 |   }
 33 |   let messageComposer = RxModal.messageComposer {
 34 |       $0.recipients = ["0639981337"]
 35 |       $0.body = "Hello World!"
 36 |   }
 37 |   contactUsButton
 38 |       .rx.tap
 39 |       .flatMapFirst { [unowned contactUsButton] in
 40 |           RxModal.actionSheet(
 41 |               source: .bounds(contactUsButton),
 42 |               actions: [
 43 |                   .default(
 44 |                       title: "Mail",
 45 |                       flatMapTo: mailComposer
 46 |                   ),
 47 |                   .default(
 48 |                       title: "Message",
 49 |                       flatMapTo: messageComposer
 50 |                   ),
 51 |                   .cancel(title: "Cancel")
 52 |               ])
 53 |       }
 54 |       .subscribe()
 55 |       .disposed(by: disposeBag)
 56 |     
57 |
59 | 60 |
63 | 64 | # Supported Modals 65 | 66 | ```swift 67 | // MFMailComposeViewController 68 | RxModal.mailComposer() -> Single 69 | 70 | // MFMessageComposeViewController 71 | RxModal.messageComposer() -> Single 72 | 73 | // MPMediaPickerController 74 | RxModal.mediaPicker() -> Single 75 | 76 | // PHPickerViewController 77 | RxModal.photoPicker() -> Single<[PHPickerResult]> 78 | 79 | // ASWebAuthenticationSession 80 | RxModal.webAuthenticationSession(url:callbackURLScheme:) -> Single 81 | 82 | // UIAlertController 83 | RxModal.alert(title:message:textFields:actions:) -> Observable 84 | RxModal.actionSheet(source:title:message:actions:) -> Observable 85 | ``` 86 | 87 | ### Presenter 88 | 89 | All these functions also include a `presenter: Presenter` argument that allows you to choose where the modal will be presented. 90 | Presenters are just lazy `UIViewController` getters: 91 | ```swift 92 | .viewController(_:) -> $0 93 | .view(_:) -> $0.window?.rootViewController 94 | .window(_:) -> $0.rootViewController 95 | .scene(_:) -> $0.windows.first?.rootViewController 96 | .keyWindow -> UIApplication.shared.keyWindow?.rootViewController 97 | ``` 98 | Default is `.keyWindow`. On iPad or macCatalyst allowing multiple windows, we discourage you to use `.keyWindow` or `.scene(_:)` as it might select the wrong window. 99 | 100 | ### Configuration 101 | 102 | These functions also include a configuration closure : `(ViewController) -> Void` that will let you configure the view controller before presentation. 103 | 104 | If a modal requires some parameters at `init` time, they will be part of the `RxModal` function (ex: `ASWebAuthenticationSession`, `UIAlertController`). 105 | 106 | ### Preconditions 107 | 108 | Some RxModals perform precondition checks before presenting the modal and emit a `RxModalError.unsupported` if they aren't fulfilled: 109 | - `RxModal.mailComposer()` → `MFMailComposeViewController.canSendMail()` 110 | - `RxModal.messageComposer()` → `MFMessageComposeViewController.canSendText()` 111 | 112 | Some RxModals perform an authorization status check before presenting the modal and either request authorization, or emit a `RxModalError.authorizationStatusDenied(Any)` if authorization is denied: 113 | - `RxModal.mediaPicker()` → `MPMediaLibrary.authorizationStatus()` 114 | 115 | ### Dialogs 116 | 117 | `RxModal.alert()` and `RxModal.actionSheet()` allows you to define actions that are converted to a new Observable stream, a value, or an error: 118 | ```swift 119 | DialogAction.default(title:flatMapTo: Observable) 120 | DialogAction.default(title:mapTo: T) // == flatMapTo: Observable.just(T) 121 | DialogAction.default(title:throw: Error) // == flatMapTo: Observable.error(Error) 122 | DialogAction.default(title:) // == flatMapTo: Observable.empty() 123 | ``` 124 | 125 | `RxModal.alert()` also let you configure alert text fields: 126 | ```swift 127 | RxModal.alert( 128 | title: "Sign in", 129 | message: "Please sign in using your credentials", 130 | textFields: [ 131 | DialogTextField.email { $0.placeholder = "e-mail" }, 132 | DialogTextField.password { $0.placeholder = "password" } 133 | ], 134 | actions: [ 135 | .cancel(title: "Cancel"), 136 | .default(title: "Sign In") { textFields in 137 | Credentials( 138 | email: textFields[0].text ?? "", 139 | password: textFields[1].text ?? "" 140 | ) 141 | }, 142 | ] 143 | ) 144 | ``` 145 | 146 | # Extending RxModal 147 | 148 | You can easily extend RxModal with your own controllers / modal flows. 149 | 150 | If your controller is already returning its output using Rx, it's easy: 151 | ```swift 152 | class MyModalViewController: UIViewController { 153 | let myResult = PublishSubject() 154 | // ... 155 | } 156 | 157 | extension RxModal { 158 | func myModal( 159 | presenter: Presenter = .keyWindow, 160 | configuration: @escaping (MyModalViewController) -> Void 161 | ) -> Single { 162 | RxModalCoordinator.present(using: presenter) { _ in 163 | let modal = MyModalViewController() 164 | configuration(modal) 165 | return modal 166 | } sequence: { 167 | $0.viewController.myResult.asSingle() 168 | } 169 | } 170 | } 171 | ``` 172 | 173 | If your controller is rather using a traditional `delegate` approach, you'll need to subclass `RxModalCoordinator`: 174 | 175 | ```swift 176 | protocol MyModalViewControllerDelegate: AnyObject { 177 | func myModal(_ myModal: MyModalViewController, didFinishWith result: MyResult) 178 | func myModal(_ myModal: MyModalViewController, didFinishWithError error: Error) 179 | } 180 | 181 | class MyModalViewController: UIViewController { 182 | weak var delegate: MyModalViewControllerDelegate? 183 | // ... 184 | } 185 | 186 | extension RxModal { 187 | func myModal( 188 | presenter: Presenter = .keyWindow, 189 | configuration: @escaping (MyModalViewController) -> Void 190 | ) -> Single { 191 | 192 | MyModalViewControllerCoordinator.present(using: presenter) { coordinator in 193 | let modal = MyModalViewController() 194 | modal.delegate = coordinator 195 | configuration(modal) 196 | return modal 197 | } sequence: { 198 | $0.myResult.asSingle() 199 | } 200 | 201 | } 202 | } 203 | 204 | final class MyModalViewControllerCoordinator: RxModalCoordinator, MyModalViewControllerDelegate { 205 | required init(){} 206 | 207 | let myResult = PublishSubject() 208 | 209 | func myModal(_ myModal: MyModalViewController, didFinishWith result: MyResult) { 210 | myResult.onNext(result) 211 | myResult.onCompleted() 212 | } 213 | 214 | func myModal(_ myModal: MyModalViewController, didFinishWithError error: Error) { 215 | myResult.onError(error) 216 | } 217 | 218 | } 219 | ``` 220 | 221 | If your controller is embedded in a non `UIViewController` object, you won't be able to leverage on `RxModalCoordinator` and you'll need to handle all the present/dismiss boilerplate. See [ASWebAuthenticationSession.swift](./Sources/Other/ASWebAuthenticationSession.swift) as an example. 222 | 223 | 224 | # Author 225 | 226 | [Jérôme Alves](https://twitter.com/jegnux) 227 | 228 | # License 229 | 230 | **RxModal** is available under the MIT license. See the [LICENSE](LICENSE) file for more info. 231 | -------------------------------------------------------------------------------- /RxModal.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "RxModal" 3 | s.version = "1.0.2" 4 | s.summary = "Subscribe to your modal flows" 5 | s.description = <<-DESC 6 | RxModal helps you handle any modal flow as a simple Observable sequence. 7 | DESC 8 | s.homepage = "https://github.com/RxSwiftCommunity/RxModal" 9 | s.license = { :type => "MIT", :file => "LICENSE" } 10 | s.author = { "Jérôme Alves" => "j.alves@me.com" } 11 | s.social_media_url = "https://twitter.com/jegnux" 12 | s.ios.deployment_target = '9.0' 13 | s.source = { :git => "https://github.com/RxSwiftCommunity/RxModal.git", :tag => s.version.to_s } 14 | s.source_files = "Sources/**/*" 15 | s.frameworks = "Foundation" 16 | 17 | s.dependency 'RxSwift', '~> 6.0' 18 | s.dependency 'RxCocoa', '~> 6.0' 19 | 20 | end 21 | -------------------------------------------------------------------------------- /RxModal.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 47; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 52D6D9871BEFF229002C0205 /* RxModal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* RxModal.framework */; }; 11 | 755A90E125D587490068D559 /* Internals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90D325D587490068D559 /* Internals.swift */; }; 12 | 755A90E225D587490068D559 /* RxModalDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90D425D587490068D559 /* RxModalDescription.swift */; }; 13 | 755A90E325D587490068D559 /* RxModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90D525D587490068D559 /* RxModal.swift */; }; 14 | 755A90E425D587490068D559 /* ASWebAuthenticationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90D725D587490068D559 /* ASWebAuthenticationSession.swift */; }; 15 | 755A90E525D587490068D559 /* Rx+AuthorizationStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90D825D587490068D559 /* Rx+AuthorizationStatus.swift */; }; 16 | 755A90E625D587490068D559 /* Presenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90D925D587490068D559 /* Presenter.swift */; }; 17 | 755A90E725D587490068D559 /* UIAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90DB25D587490068D559 /* UIAlertController.swift */; }; 18 | 755A90E825D587490068D559 /* Dialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90DC25D587490068D559 /* Dialog.swift */; }; 19 | 755A90E925D587490068D559 /* PHPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90DE25D587490068D559 /* PHPickerViewController.swift */; }; 20 | 755A90EA25D587490068D559 /* MPMediaPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90DF25D587490068D559 /* MPMediaPickerController.swift */; }; 21 | 755A90EB25D587490068D559 /* RxModalCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 755A90E025D587490068D559 /* RxModalCoordinator.swift */; }; 22 | 75F1131D25D66E4E0005DACB /* MFMessageComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75F1131B25D66E4E0005DACB /* MFMessageComposeViewController.swift */; }; 23 | 75F1131E25D66E4E0005DACB /* MFMailComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75F1131C25D66E4E0005DACB /* MFMailComposeViewController.swift */; }; 24 | 8933C7901EB5B82D000D00A4 /* RxModalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7891EB5B82A000D00A4 /* RxModalTests.swift */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 52D6D97B1BEFF229002C0205; 33 | remoteInfo = RxModal; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 52D6D97C1BEFF229002C0205 /* RxModal.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxModal.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 52D6D9861BEFF229002C0205 /* RxModal-iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RxModal-iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 755A90D325D587490068D559 /* Internals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.swift; sourceTree = ""; }; 41 | 755A90D425D587490068D559 /* RxModalDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxModalDescription.swift; sourceTree = ""; }; 42 | 755A90D525D587490068D559 /* RxModal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxModal.swift; sourceTree = ""; }; 43 | 755A90D725D587490068D559 /* ASWebAuthenticationSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASWebAuthenticationSession.swift; sourceTree = ""; }; 44 | 755A90D825D587490068D559 /* Rx+AuthorizationStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rx+AuthorizationStatus.swift"; sourceTree = ""; }; 45 | 755A90D925D587490068D559 /* Presenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presenter.swift; sourceTree = ""; }; 46 | 755A90DB25D587490068D559 /* UIAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIAlertController.swift; sourceTree = ""; }; 47 | 755A90DC25D587490068D559 /* Dialog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dialog.swift; sourceTree = ""; }; 48 | 755A90DE25D587490068D559 /* PHPickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PHPickerViewController.swift; sourceTree = ""; }; 49 | 755A90DF25D587490068D559 /* MPMediaPickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MPMediaPickerController.swift; sourceTree = ""; }; 50 | 755A90E025D587490068D559 /* RxModalCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxModalCoordinator.swift; sourceTree = ""; }; 51 | 75F1131B25D66E4E0005DACB /* MFMessageComposeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFMessageComposeViewController.swift; sourceTree = ""; }; 52 | 75F1131C25D66E4E0005DACB /* MFMailComposeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFMailComposeViewController.swift; sourceTree = ""; }; 53 | 8933C7891EB5B82A000D00A4 /* RxModalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxModalTests.swift; sourceTree = ""; }; 54 | AD2FAA261CD0B6D800659CF4 /* RxModal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = RxModal.plist; sourceTree = ""; }; 55 | AD2FAA281CD0B6E100659CF4 /* RxModalTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = RxModalTests.plist; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 52D6D9781BEFF229002C0205 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | 52D6D9831BEFF229002C0205 /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | 52D6D9871BEFF229002C0205 /* RxModal.framework in Frameworks */, 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | 52D6D9721BEFF229002C0205 = { 78 | isa = PBXGroup; 79 | children = ( 80 | 755A90D225D587490068D559 /* Sources */, 81 | 8933C7831EB5B7EB000D00A4 /* Tests */, 82 | 52D6D99C1BEFF38C002C0205 /* Configs */, 83 | 52D6D97D1BEFF229002C0205 /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | 52D6D97D1BEFF229002C0205 /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 52D6D97C1BEFF229002C0205 /* RxModal.framework */, 91 | 52D6D9861BEFF229002C0205 /* RxModal-iOS Tests.xctest */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | 52D6D99C1BEFF38C002C0205 /* Configs */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | DD7502721C68FC1B006590AF /* Frameworks */, 100 | DD7502731C68FC20006590AF /* Tests */, 101 | ); 102 | path = Configs; 103 | sourceTree = ""; 104 | }; 105 | 755A90D225D587490068D559 /* Sources */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 755A90D325D587490068D559 /* Internals.swift */, 109 | 755A90D525D587490068D559 /* RxModal.swift */, 110 | 755A90D425D587490068D559 /* RxModalDescription.swift */, 111 | 755A90E025D587490068D559 /* RxModalCoordinator.swift */, 112 | 755A90D825D587490068D559 /* Rx+AuthorizationStatus.swift */, 113 | 755A90D925D587490068D559 /* Presenter.swift */, 114 | 755A90D625D587490068D559 /* Other */, 115 | 755A90DA25D587490068D559 /* Dialogs */, 116 | 755A90DD25D587490068D559 /* Pickers */, 117 | 75F1131A25D66E4E0005DACB /* Composers */, 118 | ); 119 | path = Sources; 120 | sourceTree = ""; 121 | }; 122 | 755A90D625D587490068D559 /* Other */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 755A90D725D587490068D559 /* ASWebAuthenticationSession.swift */, 126 | ); 127 | path = Other; 128 | sourceTree = ""; 129 | }; 130 | 755A90DA25D587490068D559 /* Dialogs */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 755A90DB25D587490068D559 /* UIAlertController.swift */, 134 | 755A90DC25D587490068D559 /* Dialog.swift */, 135 | ); 136 | path = Dialogs; 137 | sourceTree = ""; 138 | }; 139 | 755A90DD25D587490068D559 /* Pickers */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 755A90DE25D587490068D559 /* PHPickerViewController.swift */, 143 | 755A90DF25D587490068D559 /* MPMediaPickerController.swift */, 144 | ); 145 | path = Pickers; 146 | sourceTree = ""; 147 | }; 148 | 75F1131A25D66E4E0005DACB /* Composers */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 75F1131B25D66E4E0005DACB /* MFMessageComposeViewController.swift */, 152 | 75F1131C25D66E4E0005DACB /* MFMailComposeViewController.swift */, 153 | ); 154 | path = Composers; 155 | sourceTree = ""; 156 | }; 157 | 8933C7831EB5B7EB000D00A4 /* Tests */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 8933C7891EB5B82A000D00A4 /* RxModalTests.swift */, 161 | ); 162 | name = Tests; 163 | path = Tests/RxModalTests; 164 | sourceTree = ""; 165 | }; 166 | DD7502721C68FC1B006590AF /* Frameworks */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | AD2FAA261CD0B6D800659CF4 /* RxModal.plist */, 170 | ); 171 | name = Frameworks; 172 | sourceTree = ""; 173 | }; 174 | DD7502731C68FC20006590AF /* Tests */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | AD2FAA281CD0B6E100659CF4 /* RxModalTests.plist */, 178 | ); 179 | name = Tests; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXGroup section */ 183 | 184 | /* Begin PBXHeadersBuildPhase section */ 185 | 52D6D9791BEFF229002C0205 /* Headers */ = { 186 | isa = PBXHeadersBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | /* End PBXHeadersBuildPhase section */ 193 | 194 | /* Begin PBXNativeTarget section */ 195 | 52D6D97B1BEFF229002C0205 /* RxModal-iOS */ = { 196 | isa = PBXNativeTarget; 197 | buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "RxModal-iOS" */; 198 | buildPhases = ( 199 | 52D6D9771BEFF229002C0205 /* Sources */, 200 | 52D6D9781BEFF229002C0205 /* Frameworks */, 201 | 52D6D9791BEFF229002C0205 /* Headers */, 202 | 52D6D97A1BEFF229002C0205 /* Resources */, 203 | ); 204 | buildRules = ( 205 | ); 206 | dependencies = ( 207 | ); 208 | name = "RxModal-iOS"; 209 | productName = RxModal; 210 | productReference = 52D6D97C1BEFF229002C0205 /* RxModal.framework */; 211 | productType = "com.apple.product-type.framework"; 212 | }; 213 | 52D6D9851BEFF229002C0205 /* RxModal-iOS Tests */ = { 214 | isa = PBXNativeTarget; 215 | buildConfigurationList = 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "RxModal-iOS Tests" */; 216 | buildPhases = ( 217 | 52D6D9821BEFF229002C0205 /* Sources */, 218 | 52D6D9831BEFF229002C0205 /* Frameworks */, 219 | 52D6D9841BEFF229002C0205 /* Resources */, 220 | ); 221 | buildRules = ( 222 | ); 223 | dependencies = ( 224 | 52D6D9891BEFF229002C0205 /* PBXTargetDependency */, 225 | ); 226 | name = "RxModal-iOS Tests"; 227 | productName = RxModalTests; 228 | productReference = 52D6D9861BEFF229002C0205 /* RxModal-iOS Tests.xctest */; 229 | productType = "com.apple.product-type.bundle.unit-test"; 230 | }; 231 | /* End PBXNativeTarget section */ 232 | 233 | /* Begin PBXProject section */ 234 | 52D6D9731BEFF229002C0205 /* Project object */ = { 235 | isa = PBXProject; 236 | attributes = { 237 | LastSwiftUpdateCheck = 0720; 238 | LastUpgradeCheck = 1020; 239 | ORGANIZATIONNAME = RxSwiftCommunity; 240 | TargetAttributes = { 241 | 52D6D97B1BEFF229002C0205 = { 242 | CreatedOnToolsVersion = 7.1; 243 | LastSwiftMigration = 1020; 244 | }; 245 | 52D6D9851BEFF229002C0205 = { 246 | CreatedOnToolsVersion = 7.1; 247 | LastSwiftMigration = 1020; 248 | }; 249 | }; 250 | }; 251 | buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "RxModal" */; 252 | compatibilityVersion = "Xcode 6.3"; 253 | developmentRegion = en; 254 | hasScannedForEncodings = 0; 255 | knownRegions = ( 256 | en, 257 | Base, 258 | ); 259 | mainGroup = 52D6D9721BEFF229002C0205; 260 | productRefGroup = 52D6D97D1BEFF229002C0205 /* Products */; 261 | projectDirPath = ""; 262 | projectRoot = ""; 263 | targets = ( 264 | 52D6D97B1BEFF229002C0205 /* RxModal-iOS */, 265 | 52D6D9851BEFF229002C0205 /* RxModal-iOS Tests */, 266 | ); 267 | }; 268 | /* End PBXProject section */ 269 | 270 | /* Begin PBXResourcesBuildPhase section */ 271 | 52D6D97A1BEFF229002C0205 /* Resources */ = { 272 | isa = PBXResourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | }; 278 | 52D6D9841BEFF229002C0205 /* Resources */ = { 279 | isa = PBXResourcesBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | }; 285 | /* End PBXResourcesBuildPhase section */ 286 | 287 | /* Begin PBXSourcesBuildPhase section */ 288 | 52D6D9771BEFF229002C0205 /* Sources */ = { 289 | isa = PBXSourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | 755A90E625D587490068D559 /* Presenter.swift in Sources */, 293 | 75F1131E25D66E4E0005DACB /* MFMailComposeViewController.swift in Sources */, 294 | 755A90E225D587490068D559 /* RxModalDescription.swift in Sources */, 295 | 755A90E125D587490068D559 /* Internals.swift in Sources */, 296 | 755A90EB25D587490068D559 /* RxModalCoordinator.swift in Sources */, 297 | 75F1131D25D66E4E0005DACB /* MFMessageComposeViewController.swift in Sources */, 298 | 755A90EA25D587490068D559 /* MPMediaPickerController.swift in Sources */, 299 | 755A90E725D587490068D559 /* UIAlertController.swift in Sources */, 300 | 755A90E525D587490068D559 /* Rx+AuthorizationStatus.swift in Sources */, 301 | 755A90E425D587490068D559 /* ASWebAuthenticationSession.swift in Sources */, 302 | 755A90E325D587490068D559 /* RxModal.swift in Sources */, 303 | 755A90E825D587490068D559 /* Dialog.swift in Sources */, 304 | 755A90E925D587490068D559 /* PHPickerViewController.swift in Sources */, 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | 52D6D9821BEFF229002C0205 /* Sources */ = { 309 | isa = PBXSourcesBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | 8933C7901EB5B82D000D00A4 /* RxModalTests.swift in Sources */, 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | /* End PBXSourcesBuildPhase section */ 317 | 318 | /* Begin PBXTargetDependency section */ 319 | 52D6D9891BEFF229002C0205 /* PBXTargetDependency */ = { 320 | isa = PBXTargetDependency; 321 | target = 52D6D97B1BEFF229002C0205 /* RxModal-iOS */; 322 | targetProxy = 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */; 323 | }; 324 | /* End PBXTargetDependency section */ 325 | 326 | /* Begin XCBuildConfiguration section */ 327 | 52D6D98E1BEFF229002C0205 /* Debug */ = { 328 | isa = XCBuildConfiguration; 329 | buildSettings = { 330 | ALWAYS_SEARCH_USER_PATHS = NO; 331 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 332 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 333 | CLANG_CXX_LIBRARY = "libc++"; 334 | CLANG_ENABLE_MODULES = YES; 335 | CLANG_ENABLE_OBJC_ARC = YES; 336 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 337 | CLANG_WARN_BOOL_CONVERSION = YES; 338 | CLANG_WARN_COMMA = YES; 339 | CLANG_WARN_CONSTANT_CONVERSION = YES; 340 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 341 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 342 | CLANG_WARN_EMPTY_BODY = YES; 343 | CLANG_WARN_ENUM_CONVERSION = YES; 344 | CLANG_WARN_INFINITE_RECURSION = YES; 345 | CLANG_WARN_INT_CONVERSION = YES; 346 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 347 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 348 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 349 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 350 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 351 | CLANG_WARN_STRICT_PROTOTYPES = YES; 352 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 353 | CLANG_WARN_UNREACHABLE_CODE = YES; 354 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 355 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 356 | COPY_PHASE_STRIP = NO; 357 | CURRENT_PROJECT_VERSION = 1; 358 | DEBUG_INFORMATION_FORMAT = dwarf; 359 | ENABLE_STRICT_OBJC_MSGSEND = YES; 360 | ENABLE_TESTABILITY = YES; 361 | GCC_C_LANGUAGE_STANDARD = gnu99; 362 | GCC_DYNAMIC_NO_PIC = NO; 363 | GCC_NO_COMMON_BLOCKS = YES; 364 | GCC_OPTIMIZATION_LEVEL = 0; 365 | GCC_PREPROCESSOR_DEFINITIONS = ( 366 | "DEBUG=1", 367 | "$(inherited)", 368 | ); 369 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 370 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 371 | GCC_WARN_UNDECLARED_SELECTOR = YES; 372 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 373 | GCC_WARN_UNUSED_FUNCTION = YES; 374 | GCC_WARN_UNUSED_VARIABLE = YES; 375 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 376 | MTL_ENABLE_DEBUG_INFO = YES; 377 | ONLY_ACTIVE_ARCH = YES; 378 | SDKROOT = iphoneos; 379 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 380 | SWIFT_VERSION = 5.0; 381 | TARGETED_DEVICE_FAMILY = "1,2"; 382 | VERSIONING_SYSTEM = "apple-generic"; 383 | VERSION_INFO_PREFIX = ""; 384 | }; 385 | name = Debug; 386 | }; 387 | 52D6D98F1BEFF229002C0205 /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | CURRENT_PROJECT_VERSION = 1; 418 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 419 | ENABLE_NS_ASSERTIONS = NO; 420 | ENABLE_STRICT_OBJC_MSGSEND = YES; 421 | GCC_C_LANGUAGE_STANDARD = gnu99; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 424 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 425 | GCC_WARN_UNDECLARED_SELECTOR = YES; 426 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 427 | GCC_WARN_UNUSED_FUNCTION = YES; 428 | GCC_WARN_UNUSED_VARIABLE = YES; 429 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 430 | MTL_ENABLE_DEBUG_INFO = NO; 431 | SDKROOT = iphoneos; 432 | SWIFT_VERSION = 5.0; 433 | TARGETED_DEVICE_FAMILY = "1,2"; 434 | VALIDATE_PRODUCT = YES; 435 | VERSIONING_SYSTEM = "apple-generic"; 436 | VERSION_INFO_PREFIX = ""; 437 | }; 438 | name = Release; 439 | }; 440 | 52D6D9911BEFF229002C0205 /* Debug */ = { 441 | isa = XCBuildConfiguration; 442 | buildSettings = { 443 | APPLICATION_EXTENSION_API_ONLY = YES; 444 | CLANG_ENABLE_MODULES = YES; 445 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 446 | DEFINES_MODULE = YES; 447 | DYLIB_COMPATIBILITY_VERSION = 1; 448 | DYLIB_CURRENT_VERSION = 1; 449 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 450 | INFOPLIST_FILE = Configs/RxModal.plist; 451 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 452 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 453 | ONLY_ACTIVE_ARCH = NO; 454 | PRODUCT_BUNDLE_IDENTIFIER = "com.RxModal.RxModal-iOS"; 455 | PRODUCT_NAME = RxModal; 456 | SKIP_INSTALL = YES; 457 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 458 | SWIFT_VERSION = 5.0; 459 | }; 460 | name = Debug; 461 | }; 462 | 52D6D9921BEFF229002C0205 /* Release */ = { 463 | isa = XCBuildConfiguration; 464 | buildSettings = { 465 | APPLICATION_EXTENSION_API_ONLY = YES; 466 | CLANG_ENABLE_MODULES = YES; 467 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 468 | DEFINES_MODULE = YES; 469 | DYLIB_COMPATIBILITY_VERSION = 1; 470 | DYLIB_CURRENT_VERSION = 1; 471 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 472 | INFOPLIST_FILE = Configs/RxModal.plist; 473 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 474 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 475 | PRODUCT_BUNDLE_IDENTIFIER = "com.RxModal.RxModal-iOS"; 476 | PRODUCT_NAME = RxModal; 477 | SKIP_INSTALL = YES; 478 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 479 | SWIFT_VERSION = 5.0; 480 | }; 481 | name = Release; 482 | }; 483 | 52D6D9941BEFF229002C0205 /* Debug */ = { 484 | isa = XCBuildConfiguration; 485 | buildSettings = { 486 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 487 | CLANG_ENABLE_MODULES = YES; 488 | INFOPLIST_FILE = Configs/RxModalTests.plist; 489 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 490 | PRODUCT_BUNDLE_IDENTIFIER = "com.RxModal.RxModal-iOS-Tests"; 491 | PRODUCT_NAME = "$(TARGET_NAME)"; 492 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 493 | SWIFT_VERSION = 5.0; 494 | }; 495 | name = Debug; 496 | }; 497 | 52D6D9951BEFF229002C0205 /* Release */ = { 498 | isa = XCBuildConfiguration; 499 | buildSettings = { 500 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 501 | CLANG_ENABLE_MODULES = YES; 502 | INFOPLIST_FILE = Configs/RxModalTests.plist; 503 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 504 | PRODUCT_BUNDLE_IDENTIFIER = "com.RxModal.RxModal-iOS-Tests"; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 507 | SWIFT_VERSION = 5.0; 508 | }; 509 | name = Release; 510 | }; 511 | /* End XCBuildConfiguration section */ 512 | 513 | /* Begin XCConfigurationList section */ 514 | 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "RxModal" */ = { 515 | isa = XCConfigurationList; 516 | buildConfigurations = ( 517 | 52D6D98E1BEFF229002C0205 /* Debug */, 518 | 52D6D98F1BEFF229002C0205 /* Release */, 519 | ); 520 | defaultConfigurationIsVisible = 0; 521 | defaultConfigurationName = Release; 522 | }; 523 | 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "RxModal-iOS" */ = { 524 | isa = XCConfigurationList; 525 | buildConfigurations = ( 526 | 52D6D9911BEFF229002C0205 /* Debug */, 527 | 52D6D9921BEFF229002C0205 /* Release */, 528 | ); 529 | defaultConfigurationIsVisible = 0; 530 | defaultConfigurationName = Release; 531 | }; 532 | 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "RxModal-iOS Tests" */ = { 533 | isa = XCConfigurationList; 534 | buildConfigurations = ( 535 | 52D6D9941BEFF229002C0205 /* Debug */, 536 | 52D6D9951BEFF229002C0205 /* Release */, 537 | ); 538 | defaultConfigurationIsVisible = 0; 539 | defaultConfigurationName = Release; 540 | }; 541 | /* End XCConfigurationList section */ 542 | }; 543 | rootObject = 52D6D9731BEFF229002C0205 /* Project object */; 544 | } 545 | -------------------------------------------------------------------------------- /RxModal.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxModal.xcodeproj/xcshareddata/xcschemes/RxModal-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /RxModal.xcodeproj/xcshareddata/xcschemes/RxModal-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /RxModal.xcodeproj/xcshareddata/xcschemes/RxModal-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /RxModal.xcodeproj/xcshareddata/xcschemes/RxModal-watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /RxModalExample/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'RxModalExample' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for RxModalExample 9 | pod 'RxModal', path: '../RxModal.podspec' 10 | end 11 | -------------------------------------------------------------------------------- /RxModalExample/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - RxCocoa (6.0.0): 3 | - RxRelay (= 6.0.0) 4 | - RxSwift (= 6.0.0) 5 | - RxModal (1.0.1): 6 | - RxCocoa (~> 6.0) 7 | - RxSwift (~> 6.0) 8 | - RxRelay (6.0.0): 9 | - RxSwift (= 6.0.0) 10 | - RxSwift (6.0.0) 11 | 12 | DEPENDENCIES: 13 | - RxModal (from `../RxModal.podspec`) 14 | 15 | SPEC REPOS: 16 | trunk: 17 | - RxCocoa 18 | - RxRelay 19 | - RxSwift 20 | 21 | EXTERNAL SOURCES: 22 | RxModal: 23 | :path: "../RxModal.podspec" 24 | 25 | SPEC CHECKSUMS: 26 | RxCocoa: 3f79328fafa3645b34600f37c31e64c73ae3a80e 27 | RxModal: abfd791d24d9bba2539d6fe1f1a179dba8170e1d 28 | RxRelay: 8d593be109c06ea850df027351beba614b012ffb 29 | RxSwift: c14e798c59b9f6e9a2df8fd235602e85cc044295 30 | 31 | PODFILE CHECKSUM: 57ce72de39d50c403818fe31c9311026bd4a55fe 32 | 33 | COCOAPODS: 1.10.1 34 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1B0BC9AB78EE118468A1EC67 /* Pods_RxModalExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 89B5467EFFDD600E8E0ECDA2 /* Pods_RxModalExample.framework */; }; 11 | 7580E40325CB49A800F9A0A7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7580E40225CB49A800F9A0A7 /* AppDelegate.swift */; }; 12 | 7580E40525CB49A800F9A0A7 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7580E40425CB49A800F9A0A7 /* SceneDelegate.swift */; }; 13 | 7580E40725CB49A800F9A0A7 /* FlowsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7580E40625CB49A800F9A0A7 /* FlowsListViewController.swift */; }; 14 | 7580E40A25CB49A800F9A0A7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7580E40825CB49A800F9A0A7 /* Main.storyboard */; }; 15 | 7580E40C25CB49A900F9A0A7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7580E40B25CB49A900F9A0A7 /* Assets.xcassets */; }; 16 | 7580E40F25CB49A900F9A0A7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7580E40D25CB49A900F9A0A7 /* LaunchScreen.storyboard */; }; 17 | 75D9706325D5438900EECC2B /* Splash in Frameworks */ = {isa = PBXBuildFile; productRef = 75D9706225D5438900EECC2B /* Splash */; }; 18 | 75D9706625D5716000EECC2B /* FlowDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75D9706525D5716000EECC2B /* FlowDetailViewController.swift */; }; 19 | 75D9706925D571C600EECC2B /* Flow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75D9706825D571C600EECC2B /* Flow.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 225B62DC46C283E24EADD0CE /* Pods-RxModalExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxModalExample.release.xcconfig"; path = "Target Support Files/Pods-RxModalExample/Pods-RxModalExample.release.xcconfig"; sourceTree = ""; }; 24 | 7580E3FF25CB49A800F9A0A7 /* RxModalExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxModalExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 7580E40225CB49A800F9A0A7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | 7580E40425CB49A800F9A0A7 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 27 | 7580E40625CB49A800F9A0A7 /* FlowsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowsListViewController.swift; sourceTree = ""; }; 28 | 7580E40925CB49A800F9A0A7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 29 | 7580E40B25CB49A900F9A0A7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 30 | 7580E40E25CB49A900F9A0A7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 31 | 7580E41025CB49A900F9A0A7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | 75BA90C325CED7BA00AF94E6 /* RxModalExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RxModalExample.entitlements; sourceTree = ""; }; 33 | 75D9706525D5716000EECC2B /* FlowDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowDetailViewController.swift; sourceTree = ""; }; 34 | 75D9706825D571C600EECC2B /* Flow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Flow.swift; sourceTree = ""; }; 35 | 89B5467EFFDD600E8E0ECDA2 /* Pods_RxModalExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxModalExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | A8FF776624B518F974CE128B /* Pods-RxModalExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxModalExample.debug.xcconfig"; path = "Target Support Files/Pods-RxModalExample/Pods-RxModalExample.debug.xcconfig"; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 7580E3FC25CB49A800F9A0A7 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | 1B0BC9AB78EE118468A1EC67 /* Pods_RxModalExample.framework in Frameworks */, 45 | 75D9706325D5438900EECC2B /* Splash in Frameworks */, 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXFrameworksBuildPhase section */ 50 | 51 | /* Begin PBXGroup section */ 52 | 31F34B4B17BEF8787CAA7124 /* Pods */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | A8FF776624B518F974CE128B /* Pods-RxModalExample.debug.xcconfig */, 56 | 225B62DC46C283E24EADD0CE /* Pods-RxModalExample.release.xcconfig */, 57 | ); 58 | path = Pods; 59 | sourceTree = ""; 60 | }; 61 | 7580E3F625CB49A800F9A0A7 = { 62 | isa = PBXGroup; 63 | children = ( 64 | 7580E40125CB49A800F9A0A7 /* RxModalExample */, 65 | 7580E40025CB49A800F9A0A7 /* Products */, 66 | 31F34B4B17BEF8787CAA7124 /* Pods */, 67 | 94219E9B0A6AD0135DCD89B8 /* Frameworks */, 68 | ); 69 | sourceTree = ""; 70 | }; 71 | 7580E40025CB49A800F9A0A7 /* Products */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 7580E3FF25CB49A800F9A0A7 /* RxModalExample.app */, 75 | ); 76 | name = Products; 77 | sourceTree = ""; 78 | }; 79 | 7580E40125CB49A800F9A0A7 /* RxModalExample */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 75BA90C325CED7BA00AF94E6 /* RxModalExample.entitlements */, 83 | 7580E40225CB49A800F9A0A7 /* AppDelegate.swift */, 84 | 7580E40425CB49A800F9A0A7 /* SceneDelegate.swift */, 85 | 75D9706825D571C600EECC2B /* Flow.swift */, 86 | 7580E40625CB49A800F9A0A7 /* FlowsListViewController.swift */, 87 | 75D9706525D5716000EECC2B /* FlowDetailViewController.swift */, 88 | 7580E40825CB49A800F9A0A7 /* Main.storyboard */, 89 | 7580E40B25CB49A900F9A0A7 /* Assets.xcassets */, 90 | 7580E40D25CB49A900F9A0A7 /* LaunchScreen.storyboard */, 91 | 7580E41025CB49A900F9A0A7 /* Info.plist */, 92 | ); 93 | path = RxModalExample; 94 | sourceTree = ""; 95 | }; 96 | 94219E9B0A6AD0135DCD89B8 /* Frameworks */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 89B5467EFFDD600E8E0ECDA2 /* Pods_RxModalExample.framework */, 100 | ); 101 | name = Frameworks; 102 | sourceTree = ""; 103 | }; 104 | /* End PBXGroup section */ 105 | 106 | /* Begin PBXNativeTarget section */ 107 | 7580E3FE25CB49A800F9A0A7 /* RxModalExample */ = { 108 | isa = PBXNativeTarget; 109 | buildConfigurationList = 7580E41325CB49A900F9A0A7 /* Build configuration list for PBXNativeTarget "RxModalExample" */; 110 | buildPhases = ( 111 | 3E3353F6ABD85963532BD1ED /* [CP] Check Pods Manifest.lock */, 112 | 7580E3FB25CB49A800F9A0A7 /* Sources */, 113 | 7580E3FC25CB49A800F9A0A7 /* Frameworks */, 114 | 7580E3FD25CB49A800F9A0A7 /* Resources */, 115 | 9DA9BB08D94F4D7740920AEF /* [CP] Embed Pods Frameworks */, 116 | ); 117 | buildRules = ( 118 | ); 119 | dependencies = ( 120 | ); 121 | name = RxModalExample; 122 | packageProductDependencies = ( 123 | 75D9706225D5438900EECC2B /* Splash */, 124 | ); 125 | productName = RxModalExample; 126 | productReference = 7580E3FF25CB49A800F9A0A7 /* RxModalExample.app */; 127 | productType = "com.apple.product-type.application"; 128 | }; 129 | /* End PBXNativeTarget section */ 130 | 131 | /* Begin PBXProject section */ 132 | 7580E3F725CB49A800F9A0A7 /* Project object */ = { 133 | isa = PBXProject; 134 | attributes = { 135 | LastSwiftUpdateCheck = 1230; 136 | LastUpgradeCheck = 1230; 137 | TargetAttributes = { 138 | 7580E3FE25CB49A800F9A0A7 = { 139 | CreatedOnToolsVersion = 12.3; 140 | }; 141 | }; 142 | }; 143 | buildConfigurationList = 7580E3FA25CB49A800F9A0A7 /* Build configuration list for PBXProject "RxModalExample" */; 144 | compatibilityVersion = "Xcode 9.3"; 145 | developmentRegion = en; 146 | hasScannedForEncodings = 0; 147 | knownRegions = ( 148 | en, 149 | Base, 150 | ); 151 | mainGroup = 7580E3F625CB49A800F9A0A7; 152 | packageReferences = ( 153 | 75D9706125D5438900EECC2B /* XCRemoteSwiftPackageReference "Splash" */, 154 | ); 155 | productRefGroup = 7580E40025CB49A800F9A0A7 /* Products */; 156 | projectDirPath = ""; 157 | projectRoot = ""; 158 | targets = ( 159 | 7580E3FE25CB49A800F9A0A7 /* RxModalExample */, 160 | ); 161 | }; 162 | /* End PBXProject section */ 163 | 164 | /* Begin PBXResourcesBuildPhase section */ 165 | 7580E3FD25CB49A800F9A0A7 /* Resources */ = { 166 | isa = PBXResourcesBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | 7580E40F25CB49A900F9A0A7 /* LaunchScreen.storyboard in Resources */, 170 | 7580E40C25CB49A900F9A0A7 /* Assets.xcassets in Resources */, 171 | 7580E40A25CB49A800F9A0A7 /* Main.storyboard in Resources */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXResourcesBuildPhase section */ 176 | 177 | /* Begin PBXShellScriptBuildPhase section */ 178 | 3E3353F6ABD85963532BD1ED /* [CP] Check Pods Manifest.lock */ = { 179 | isa = PBXShellScriptBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | ); 183 | inputFileListPaths = ( 184 | ); 185 | inputPaths = ( 186 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 187 | "${PODS_ROOT}/Manifest.lock", 188 | ); 189 | name = "[CP] Check Pods Manifest.lock"; 190 | outputFileListPaths = ( 191 | ); 192 | outputPaths = ( 193 | "$(DERIVED_FILE_DIR)/Pods-RxModalExample-checkManifestLockResult.txt", 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | shellPath = /bin/sh; 197 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 198 | showEnvVarsInLog = 0; 199 | }; 200 | 9DA9BB08D94F4D7740920AEF /* [CP] Embed Pods Frameworks */ = { 201 | isa = PBXShellScriptBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | ); 205 | inputFileListPaths = ( 206 | "${PODS_ROOT}/Target Support Files/Pods-RxModalExample/Pods-RxModalExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", 207 | ); 208 | name = "[CP] Embed Pods Frameworks"; 209 | outputFileListPaths = ( 210 | "${PODS_ROOT}/Target Support Files/Pods-RxModalExample/Pods-RxModalExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | shellPath = /bin/sh; 214 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RxModalExample/Pods-RxModalExample-frameworks.sh\"\n"; 215 | showEnvVarsInLog = 0; 216 | }; 217 | /* End PBXShellScriptBuildPhase section */ 218 | 219 | /* Begin PBXSourcesBuildPhase section */ 220 | 7580E3FB25CB49A800F9A0A7 /* Sources */ = { 221 | isa = PBXSourcesBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | 7580E40725CB49A800F9A0A7 /* FlowsListViewController.swift in Sources */, 225 | 75D9706925D571C600EECC2B /* Flow.swift in Sources */, 226 | 7580E40325CB49A800F9A0A7 /* AppDelegate.swift in Sources */, 227 | 7580E40525CB49A800F9A0A7 /* SceneDelegate.swift in Sources */, 228 | 75D9706625D5716000EECC2B /* FlowDetailViewController.swift in Sources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXSourcesBuildPhase section */ 233 | 234 | /* Begin PBXVariantGroup section */ 235 | 7580E40825CB49A800F9A0A7 /* Main.storyboard */ = { 236 | isa = PBXVariantGroup; 237 | children = ( 238 | 7580E40925CB49A800F9A0A7 /* Base */, 239 | ); 240 | name = Main.storyboard; 241 | sourceTree = ""; 242 | }; 243 | 7580E40D25CB49A900F9A0A7 /* LaunchScreen.storyboard */ = { 244 | isa = PBXVariantGroup; 245 | children = ( 246 | 7580E40E25CB49A900F9A0A7 /* Base */, 247 | ); 248 | name = LaunchScreen.storyboard; 249 | sourceTree = ""; 250 | }; 251 | /* End PBXVariantGroup section */ 252 | 253 | /* Begin XCBuildConfiguration section */ 254 | 7580E41125CB49A900F9A0A7 /* Debug */ = { 255 | isa = XCBuildConfiguration; 256 | buildSettings = { 257 | ALWAYS_SEARCH_USER_PATHS = NO; 258 | CLANG_ANALYZER_NONNULL = YES; 259 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 261 | CLANG_CXX_LIBRARY = "libc++"; 262 | CLANG_ENABLE_MODULES = YES; 263 | CLANG_ENABLE_OBJC_ARC = YES; 264 | CLANG_ENABLE_OBJC_WEAK = YES; 265 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 266 | CLANG_WARN_BOOL_CONVERSION = YES; 267 | CLANG_WARN_COMMA = YES; 268 | CLANG_WARN_CONSTANT_CONVERSION = YES; 269 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 270 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 271 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 272 | CLANG_WARN_EMPTY_BODY = YES; 273 | CLANG_WARN_ENUM_CONVERSION = YES; 274 | CLANG_WARN_INFINITE_RECURSION = YES; 275 | CLANG_WARN_INT_CONVERSION = YES; 276 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 277 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 278 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 281 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 282 | CLANG_WARN_STRICT_PROTOTYPES = YES; 283 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 284 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 285 | CLANG_WARN_UNREACHABLE_CODE = YES; 286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 287 | COPY_PHASE_STRIP = NO; 288 | DEBUG_INFORMATION_FORMAT = dwarf; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | ENABLE_TESTABILITY = YES; 291 | GCC_C_LANGUAGE_STANDARD = gnu11; 292 | GCC_DYNAMIC_NO_PIC = NO; 293 | GCC_NO_COMMON_BLOCKS = YES; 294 | GCC_OPTIMIZATION_LEVEL = 0; 295 | GCC_PREPROCESSOR_DEFINITIONS = ( 296 | "DEBUG=1", 297 | "$(inherited)", 298 | ); 299 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 300 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 301 | GCC_WARN_UNDECLARED_SELECTOR = YES; 302 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 303 | GCC_WARN_UNUSED_FUNCTION = YES; 304 | GCC_WARN_UNUSED_VARIABLE = YES; 305 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 306 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 307 | MTL_FAST_MATH = YES; 308 | ONLY_ACTIVE_ARCH = YES; 309 | SDKROOT = iphoneos; 310 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 311 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 312 | }; 313 | name = Debug; 314 | }; 315 | 7580E41225CB49A900F9A0A7 /* Release */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 322 | CLANG_CXX_LIBRARY = "libc++"; 323 | CLANG_ENABLE_MODULES = YES; 324 | CLANG_ENABLE_OBJC_ARC = YES; 325 | CLANG_ENABLE_OBJC_WEAK = YES; 326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_COMMA = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 341 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 342 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 343 | CLANG_WARN_STRICT_PROTOTYPES = YES; 344 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 345 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 346 | CLANG_WARN_UNREACHABLE_CODE = YES; 347 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 348 | COPY_PHASE_STRIP = NO; 349 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 350 | ENABLE_NS_ASSERTIONS = NO; 351 | ENABLE_STRICT_OBJC_MSGSEND = YES; 352 | GCC_C_LANGUAGE_STANDARD = gnu11; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 356 | GCC_WARN_UNDECLARED_SELECTOR = YES; 357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 358 | GCC_WARN_UNUSED_FUNCTION = YES; 359 | GCC_WARN_UNUSED_VARIABLE = YES; 360 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 361 | MTL_ENABLE_DEBUG_INFO = NO; 362 | MTL_FAST_MATH = YES; 363 | SDKROOT = iphoneos; 364 | SWIFT_COMPILATION_MODE = wholemodule; 365 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 366 | VALIDATE_PRODUCT = YES; 367 | }; 368 | name = Release; 369 | }; 370 | 7580E41425CB49A900F9A0A7 /* Debug */ = { 371 | isa = XCBuildConfiguration; 372 | baseConfigurationReference = A8FF776624B518F974CE128B /* Pods-RxModalExample.debug.xcconfig */; 373 | buildSettings = { 374 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 375 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 376 | CODE_SIGN_ENTITLEMENTS = RxModalExample/RxModalExample.entitlements; 377 | CODE_SIGN_IDENTITY = "Apple Development"; 378 | CODE_SIGN_STYLE = Automatic; 379 | DEVELOPMENT_TEAM = JKFCB4CN7C; 380 | INFOPLIST_FILE = RxModalExample/Info.plist; 381 | LD_RUNPATH_SEARCH_PATHS = ( 382 | "$(inherited)", 383 | "@executable_path/Frameworks", 384 | ); 385 | PRODUCT_BUNDLE_IDENTIFIER = com.RxSwiftCommunity.RxModalExample; 386 | PRODUCT_NAME = "$(TARGET_NAME)"; 387 | PROVISIONING_PROFILE_SPECIFIER = ""; 388 | SUPPORTS_MACCATALYST = YES; 389 | SWIFT_VERSION = 5.0; 390 | TARGETED_DEVICE_FAMILY = "1,2"; 391 | }; 392 | name = Debug; 393 | }; 394 | 7580E41525CB49A900F9A0A7 /* Release */ = { 395 | isa = XCBuildConfiguration; 396 | baseConfigurationReference = 225B62DC46C283E24EADD0CE /* Pods-RxModalExample.release.xcconfig */; 397 | buildSettings = { 398 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 399 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 400 | CODE_SIGN_ENTITLEMENTS = RxModalExample/RxModalExample.entitlements; 401 | CODE_SIGN_IDENTITY = "Apple Development"; 402 | CODE_SIGN_STYLE = Automatic; 403 | DEVELOPMENT_TEAM = JKFCB4CN7C; 404 | INFOPLIST_FILE = RxModalExample/Info.plist; 405 | LD_RUNPATH_SEARCH_PATHS = ( 406 | "$(inherited)", 407 | "@executable_path/Frameworks", 408 | ); 409 | PRODUCT_BUNDLE_IDENTIFIER = com.RxSwiftCommunity.RxModalExample; 410 | PRODUCT_NAME = "$(TARGET_NAME)"; 411 | PROVISIONING_PROFILE_SPECIFIER = ""; 412 | SUPPORTS_MACCATALYST = YES; 413 | SWIFT_VERSION = 5.0; 414 | TARGETED_DEVICE_FAMILY = "1,2"; 415 | }; 416 | name = Release; 417 | }; 418 | /* End XCBuildConfiguration section */ 419 | 420 | /* Begin XCConfigurationList section */ 421 | 7580E3FA25CB49A800F9A0A7 /* Build configuration list for PBXProject "RxModalExample" */ = { 422 | isa = XCConfigurationList; 423 | buildConfigurations = ( 424 | 7580E41125CB49A900F9A0A7 /* Debug */, 425 | 7580E41225CB49A900F9A0A7 /* Release */, 426 | ); 427 | defaultConfigurationIsVisible = 0; 428 | defaultConfigurationName = Release; 429 | }; 430 | 7580E41325CB49A900F9A0A7 /* Build configuration list for PBXNativeTarget "RxModalExample" */ = { 431 | isa = XCConfigurationList; 432 | buildConfigurations = ( 433 | 7580E41425CB49A900F9A0A7 /* Debug */, 434 | 7580E41525CB49A900F9A0A7 /* Release */, 435 | ); 436 | defaultConfigurationIsVisible = 0; 437 | defaultConfigurationName = Release; 438 | }; 439 | /* End XCConfigurationList section */ 440 | 441 | /* Begin XCRemoteSwiftPackageReference section */ 442 | 75D9706125D5438900EECC2B /* XCRemoteSwiftPackageReference "Splash" */ = { 443 | isa = XCRemoteSwiftPackageReference; 444 | repositoryURL = "https://github.com/JohnSundell/Splash"; 445 | requirement = { 446 | kind = upToNextMajorVersion; 447 | minimumVersion = 0.15.0; 448 | }; 449 | }; 450 | /* End XCRemoteSwiftPackageReference section */ 451 | 452 | /* Begin XCSwiftPackageProductDependency section */ 453 | 75D9706225D5438900EECC2B /* Splash */ = { 454 | isa = XCSwiftPackageProductDependency; 455 | package = 75D9706125D5438900EECC2B /* XCRemoteSwiftPackageReference "Splash" */; 456 | productName = Splash; 457 | }; 458 | /* End XCSwiftPackageProductDependency section */ 459 | }; 460 | rootObject = 7580E3F725CB49A800F9A0A7 /* Project object */; 461 | } 462 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 21 | 22 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Splash", 6 | "repositoryURL": "https://github.com/JohnSundell/Splash", 7 | "state": { 8 | "branch": null, 9 | "revision": "81de0389558ad40579027735841593b21a511fa8", 10 | "version": "0.15.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RxModalExample 4 | // 5 | // Created by Jérôme Alves on 03/02/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "icon_60pt@2x.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "60x60" 38 | }, 39 | { 40 | "filename" : "icon_60pt@3x.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "scale" : "1x", 48 | "size" : "20x20" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "scale" : "2x", 53 | "size" : "20x20" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "scale" : "1x", 58 | "size" : "29x29" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "scale" : "2x", 63 | "size" : "29x29" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "scale" : "1x", 68 | "size" : "40x40" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "40x40" 74 | }, 75 | { 76 | "filename" : "icon_76pt.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "76x76" 80 | }, 81 | { 82 | "filename" : "icon_76pt@2x.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "76x76" 86 | }, 87 | { 88 | "filename" : "icon_83.5@2x.png", 89 | "idiom" : "ipad", 90 | "scale" : "2x", 91 | "size" : "83.5x83.5" 92 | }, 93 | { 94 | "filename" : "Icon.png", 95 | "idiom" : "ios-marketing", 96 | "scale" : "1x", 97 | "size" : "1024x1024" 98 | }, 99 | { 100 | "idiom" : "mac", 101 | "scale" : "1x", 102 | "size" : "16x16" 103 | }, 104 | { 105 | "idiom" : "mac", 106 | "scale" : "2x", 107 | "size" : "16x16" 108 | }, 109 | { 110 | "idiom" : "mac", 111 | "scale" : "1x", 112 | "size" : "32x32" 113 | }, 114 | { 115 | "idiom" : "mac", 116 | "scale" : "2x", 117 | "size" : "32x32" 118 | }, 119 | { 120 | "filename" : "icon_128pt.png", 121 | "idiom" : "mac", 122 | "scale" : "1x", 123 | "size" : "128x128" 124 | }, 125 | { 126 | "filename" : "icon_256pt-1.png", 127 | "idiom" : "mac", 128 | "scale" : "2x", 129 | "size" : "128x128" 130 | }, 131 | { 132 | "filename" : "icon_256pt.png", 133 | "idiom" : "mac", 134 | "scale" : "1x", 135 | "size" : "256x256" 136 | }, 137 | { 138 | "filename" : "icon_512pt-1.png", 139 | "idiom" : "mac", 140 | "scale" : "2x", 141 | "size" : "256x256" 142 | }, 143 | { 144 | "filename" : "icon_512pt.png", 145 | "idiom" : "mac", 146 | "scale" : "1x", 147 | "size" : "512x512" 148 | }, 149 | { 150 | "filename" : "icon_512pt@2x.png", 151 | "idiom" : "mac", 152 | "scale" : "2x", 153 | "size" : "512x512" 154 | } 155 | ], 156 | "info" : { 157 | "author" : "xcode", 158 | "version" : 1 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_128pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_128pt.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_256pt-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_256pt-1.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_256pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_256pt.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_512pt-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_512pt-1.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_512pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_512pt.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_512pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_512pt@2x.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_60pt@2x.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_60pt@3x.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_76pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_76pt.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_76pt@2x.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/RxModalExample/RxModalExample/Assets.xcassets/AppIcon.appiconset/icon_83.5@2x.png -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Flow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Flow.swift 3 | // RxModalExample 4 | // 5 | // Created by Jérôme Alves on 11/02/2021. 6 | // 7 | 8 | import UIKit 9 | import RxSwift 10 | import RxModal 11 | 12 | struct Flow { 13 | let title: String 14 | let source: (UIBarButtonItem) -> Observable 15 | 16 | init(_ title: String, source: @escaping () -> O) { 17 | self = .init(title, source: { _ in source() }) 18 | } 19 | 20 | init(_ title: String, source: @escaping (UIBarButtonItem) -> O) { 21 | self.title = title 22 | self.source = { sender in 23 | source(sender) 24 | .asObservable() 25 | .map { String(rxModalDescribing: $0) } 26 | .materialize() 27 | .map { ".\($0)" } 28 | .startWith( 29 | "\(O.self)" 30 | .replacingOccurrences(of: "PrimitiveSequence<", with: "") 31 | .replacingOccurrences(of: "Trait, ", with: "<"), 32 | ".subscribe" 33 | ) 34 | .concat(Single.just(".dispose")) 35 | } 36 | } 37 | 38 | static var allFlows: [Flow] = { 39 | var flows = [ 40 | Flow("Alert") { 41 | RxModal.alert( 42 | AlertResult.self, 43 | title: "Delete Item", 44 | message: "Are you sure you want to delete something?", 45 | actions: [ 46 | .cancel(title: "Cancel", mapTo: .cancel), 47 | .destructive(title: "Delete", mapTo: .delete) 48 | ] 49 | ) 50 | }, 51 | Flow("Action Sheet") { sender in 52 | RxModal.actionSheet( 53 | AlertResult.self, 54 | source: .barButtonItem(sender), 55 | title: "Delete Item", 56 | message: "Are you sure you want to delete something?", 57 | actions: [ 58 | .cancel(title: "Cancel", mapTo: .cancel), 59 | .destructive(title: "Delete", mapTo: .delete) 60 | ] 61 | ) 62 | }, 63 | Flow("Media Picker") { 64 | RxModal.mediaPicker { 65 | $0.allowsPickingMultipleItems = true 66 | }.map(\.items) 67 | }, 68 | Flow("Mail Composer") { 69 | RxModal.mailComposer { 70 | $0.setToRecipients(["rxmodal@rxswiftcommunity.org"]) 71 | $0.setSubject("RxModal") 72 | $0.setMessageBody(""" 73 | Hey, 74 | This library is awesome! 75 | Thanks :) 76 | """, isHTML: false) 77 | } 78 | }, 79 | Flow("Message Composer") { 80 | RxModal.messageComposer { 81 | $0.recipients = ["0639981337"] 82 | $0.body = """ 83 | Hey, 84 | This library is awesome! 85 | Thanks :) 86 | """ 87 | } 88 | }, 89 | Flow("Composer Chooser") { sender in 90 | RxModal.actionSheet( 91 | source: .barButtonItem(sender), 92 | title: "Contact Us", 93 | actions: [ 94 | .default(title: "Mail", flatMapTo: RxModal.mailComposer { 95 | $0.setToRecipients(["rxmodal@rxswiftcommunity.org"]) 96 | $0.setMessageBody("Hello World!", isHTML: false) 97 | }), 98 | .default(title: "Message", flatMapTo: RxModal.messageComposer { 99 | $0.recipients = ["0639981337"] 100 | $0.body = "Hello World!" 101 | }), 102 | .cancel(title: "Cancel") 103 | ] 104 | ) 105 | }, 106 | Flow("Sign In alert") { 107 | RxModal.alert( 108 | title: "Sign in", 109 | message: "Please sign in using your credentials", 110 | textFields: [ 111 | DialogTextField.email { $0.placeholder = "e-mail" }, 112 | DialogTextField.password { $0.placeholder = "password" } 113 | ], 114 | actions: [ 115 | .cancel(title: "Cancel"), 116 | .default(title: "Sign In") { textFields in 117 | Credentials( 118 | email: textFields[0].text ?? "", 119 | password: textFields[1].text ?? "" 120 | ) 121 | }, 122 | ] 123 | ) 124 | } 125 | ] 126 | 127 | #if targetEnvironment(macCatalyst) 128 | // OAuth playgrounds seem to work only on macOS 🤷🏻‍♂️ 129 | flows.append( 130 | Flow("Web Session") { () -> Single in 131 | RxModal.alert( 132 | String?.self, 133 | title: "Client ID", 134 | message: "You must register a playground client on oauth.com first", 135 | textFields: [DialogTextField { $0.placeholder = "CLIENT ID" }], 136 | actions: [ 137 | .cancel(title: "Cancel", throw: WebSessionError.missingClientID), 138 | .default(title: "Register New Client", mapTo: nil), 139 | .preferred(.default(title: "Continue", map: { $0.first?.text })), 140 | ]) 141 | .asSingle() 142 | .flatMap { clientID in 143 | guard let clientID = clientID else { 144 | UIApplication.shared.open(URL(string: "https://www.oauth.com/playground/client-registration.html")!) 145 | throw WebSessionError.missingClientID 146 | } 147 | return RxModal.webAuthenticationSession( 148 | url: URL(string: "https://www.oauth.com/playground/auth-dialog.html?response_type=token&client_id=\(clientID)&redirect_uri=rx-modal-example://&scope=photo&state=NESR37xhi7JGQ6xI")!, 149 | callbackURLScheme: "rx-modal-example" 150 | ) 151 | } 152 | } 153 | ) 154 | #endif 155 | 156 | if #available(iOS 14, *) { 157 | flows.append( 158 | Flow("Photo Picker") { 159 | RxModal.photoPicker { 160 | $0.selectionLimit = 3 161 | } 162 | } 163 | ) 164 | } 165 | 166 | return flows 167 | }() 168 | 169 | } 170 | 171 | enum AlertResult { 172 | case delete, cancel 173 | } 174 | 175 | enum WebSessionError: Error { 176 | case missingClientID 177 | } 178 | 179 | struct Credentials { 180 | let email: String 181 | let password: String 182 | } 183 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/FlowDetailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlowDetailViewController.swift 3 | // RxModalExample 4 | // 5 | // Created by Jérôme Alves on 11/02/2021. 6 | // 7 | 8 | import UIKit 9 | import Splash 10 | import RxSwift 11 | 12 | final class FlowDetailViewController: UIViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | navigationItem.title = flow?.title 17 | navigationItem.rightBarButtonItem = startBarButtonItem 18 | flowOutputTextView.backgroundColor = format.theme.backgroundColor 19 | flowOutputTextView.textColor = .white 20 | flowOutputTextView.font = UIFont.monospacedSystemFont(ofSize: 17, weight: .regular) 21 | } 22 | 23 | deinit { 24 | flowDisposable?.dispose() 25 | } 26 | 27 | // MARK: - Output Text View 28 | 29 | @IBOutlet weak var flowOutputTextView: UITextView! 30 | 31 | private let format = AttributedStringOutputFormat(theme: .sundellsColors(withFont: Font(size: 17))) 32 | private lazy var highlighter = SyntaxHighlighter(format: format) 33 | 34 | // MARK: - Flow Lifecycle 35 | 36 | var flow: Flow? 37 | 38 | @IBOutlet weak var startBarButtonItem: UIBarButtonItem! 39 | 40 | @IBAction func startFlow(_ sender: Any) { 41 | flowDisposable = flow? 42 | .source(startBarButtonItem) 43 | .scan("") { (result, event) -> String in 44 | [result, event] 45 | .filter { $0.isEmpty == false } 46 | .joined(separator: "\n\n") 47 | } 48 | .map(highlighter.highlight) 49 | .bind(to: flowOutputTextView.rx.attributedText) 50 | } 51 | 52 | private var flowDisposable: Disposable? { 53 | didSet { oldValue?.dispose() } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/FlowsListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RxModalExample 4 | // 5 | // Created by Jérôme Alves on 03/02/2021. 6 | // 7 | 8 | import UIKit 9 | import RxModal 10 | import RxSwift 11 | import PhotosUI 12 | import RxCocoa 13 | 14 | class FlowsListViewController: UITableViewController { 15 | 16 | // MARK: - Lifecycle 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | } 21 | 22 | override func viewWillAppear(_ animated: Bool) { 23 | super.viewWillAppear(animated) 24 | transitionCoordinator?.animate(alongsideTransition: { _ in 25 | for indexPath in self.tableView.indexPathsForSelectedRows ?? [] { 26 | self.tableView.deselectRow(at: indexPath, animated: true) 27 | } 28 | }, completion: nil) 29 | } 30 | 31 | // MARK: - Table View 32 | 33 | override func numberOfSections(in tableView: UITableView) -> Int { 34 | 1 35 | } 36 | 37 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 38 | Flow.allFlows.count 39 | } 40 | 41 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 42 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! FlowCell 43 | cell.flow = Flow.allFlows[indexPath.row] 44 | return cell 45 | } 46 | 47 | // MARK: - Segues 48 | 49 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 50 | super.prepare(for: segue, sender: sender) 51 | 52 | guard 53 | let navigationController = segue.destination as? UINavigationController, 54 | let flowDetail = navigationController.topViewController as? FlowDetailViewController, 55 | let cell = sender as? FlowCell else { 56 | return 57 | } 58 | 59 | flowDetail.flow = cell.flow 60 | } 61 | } 62 | 63 | final class FlowCell: UITableViewCell { 64 | var flow: Flow? { 65 | didSet { 66 | textLabel?.text = flow?.title 67 | accessoryType = .disclosureIndicator 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | RxModal 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleURLTypes 20 | 21 | 22 | CFBundleTypeRole 23 | Viewer 24 | CFBundleURLName 25 | app 26 | CFBundleURLSchemes 27 | 28 | rx-modal-example 29 | 30 | 31 | 32 | CFBundleVersion 33 | 1 34 | LSRequiresIPhoneOS 35 | 36 | NSAppleMusicUsageDescription 37 | RxModal Demo 38 | UIApplicationSceneManifest 39 | 40 | UIApplicationSupportsMultipleScenes 41 | 42 | UISceneConfigurations 43 | 44 | UIWindowSceneSessionRoleApplication 45 | 46 | 47 | UISceneConfigurationName 48 | Default Configuration 49 | UISceneDelegateClassName 50 | $(PRODUCT_MODULE_NAME).SceneDelegate 51 | UISceneStoryboardFile 52 | Main 53 | 54 | 55 | 56 | 57 | UIApplicationSupportsIndirectInputEvents 58 | 59 | UILaunchStoryboardName 60 | LaunchScreen 61 | UIMainStoryboardFile 62 | Main 63 | UIRequiredDeviceCapabilities 64 | 65 | armv7 66 | 67 | UISupportedInterfaceOrientations 68 | 69 | UIInterfaceOrientationPortrait 70 | UIInterfaceOrientationLandscapeLeft 71 | UIInterfaceOrientationLandscapeRight 72 | 73 | UISupportedInterfaceOrientations~ipad 74 | 75 | UIInterfaceOrientationPortrait 76 | UIInterfaceOrientationPortraitUpsideDown 77 | UIInterfaceOrientationLandscapeLeft 78 | UIInterfaceOrientationLandscapeRight 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/RxModalExample.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RxModalExample/RxModalExample/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // RxModalExample 4 | // 5 | // Created by Jérôme Alves on 03/02/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Sources/Composers/MFMailComposeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MFMailComposeViewController.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 06/02/2021. 6 | // 7 | 8 | #if canImport(MessageUI) 9 | import UIKit 10 | import MessageUI 11 | import RxSwift 12 | 13 | extension RxModal { 14 | 15 | public static func mailComposer(presenter: Presenter = .keyWindow, configuration: @escaping (MFMailComposeViewController) -> Void = { _ in }) -> Single { 16 | .deferred { 17 | guard MFMailComposeViewController.canSendMail() else { 18 | throw RxModalError.unsupported 19 | } 20 | return MFMailComposeViewControllerCoordinator.present(using: presenter) { delegate in 21 | MFMailComposeViewController()..{ 22 | configuration($0) 23 | $0.mailComposeDelegate = delegate 24 | } 25 | } sequence: { 26 | $0.composerResult.asSingle() 27 | } 28 | } 29 | } 30 | } 31 | 32 | private class MFMailComposeViewControllerCoordinator: RxModalCoordinator, MFMailComposeViewControllerDelegate { 33 | required init() {} 34 | 35 | let composerResult = PublishSubject() 36 | 37 | func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { 38 | if let error = error { 39 | composerResult.onError(error) 40 | } else { 41 | composerResult.onNext(result) 42 | composerResult.onCompleted() 43 | } 44 | } 45 | } 46 | #endif 47 | -------------------------------------------------------------------------------- /Sources/Composers/MFMessageComposeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MFMailComposeViewController.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 06/02/2021. 6 | // 7 | 8 | #if canImport(MessageUI) 9 | import UIKit 10 | import MessageUI 11 | import RxSwift 12 | 13 | extension RxModal { 14 | 15 | public static func messageComposer(presenter: Presenter = .keyWindow, configuration: @escaping (MFMessageComposeViewController) -> Void = { _ in }) -> Single { 16 | .deferred { 17 | guard MFMessageComposeViewController.canSendText() else { 18 | throw RxModalError.unsupported 19 | } 20 | return MFMessageComposeViewControllerCoordinator.present(using: presenter) { delegate in 21 | MFMessageComposeViewController()..{ 22 | configuration($0) 23 | $0.messageComposeDelegate = delegate 24 | } 25 | } sequence: { 26 | $0.composerResult.asSingle() 27 | } 28 | } 29 | } 30 | } 31 | 32 | private class MFMessageComposeViewControllerCoordinator: RxModalCoordinator, MFMessageComposeViewControllerDelegate { 33 | required init() {} 34 | 35 | let composerResult = PublishSubject() 36 | 37 | func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) { 38 | composerResult.onNext(result) 39 | composerResult.onCompleted() 40 | } 41 | } 42 | #endif 43 | -------------------------------------------------------------------------------- /Sources/Dialogs/Dialog.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dialog.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 07/02/2021. 6 | // 7 | 8 | import UIKit 9 | import RxSwift 10 | 11 | public struct Dialog { 12 | public let title: String? 13 | public let message: String? 14 | public let textFields: [DialogTextField] 15 | public let actions: [DialogAction] 16 | 17 | public init(title: String? = nil, message: String? = nil, textFields: [DialogTextField] = [], actions: [DialogAction]) { 18 | assert(actions.isEmpty == false, "Must have at least one action") 19 | 20 | self.title = title 21 | self.message = message 22 | self.textFields = textFields 23 | self.actions = actions 24 | } 25 | } 26 | 27 | public enum DialogActionStyle { 28 | case `default` 29 | case cancel 30 | case destructive 31 | } 32 | 33 | public struct DialogTextField { 34 | internal let configuration: (UITextField) -> () 35 | public init(configuration: @escaping (UITextField) -> () = { _ in }) { 36 | self.configuration = configuration 37 | } 38 | 39 | public static func email(configuration: @escaping (UITextField) -> () = { _ in }) -> DialogTextField { 40 | DialogTextField { 41 | if #available(iOS 10.0, *) { 42 | $0.textContentType = .emailAddress 43 | } 44 | $0.keyboardType = .emailAddress 45 | $0.autocapitalizationType = .none 46 | $0.spellCheckingType = .no 47 | configuration($0) 48 | } 49 | } 50 | 51 | public static func password(configuration: @escaping (UITextField) -> () = { _ in }) -> DialogTextField { 52 | DialogTextField { 53 | if #available(iOS 11.0, *) { 54 | $0.textContentType = .password 55 | } 56 | $0.isSecureTextEntry = true 57 | $0.autocapitalizationType = .none 58 | $0.autocorrectionType = .no 59 | $0.spellCheckingType = .no 60 | configuration($0) 61 | } 62 | } 63 | 64 | public static func phoneNumber(configuration: @escaping (UITextField) -> () = { _ in }) -> DialogTextField { 65 | DialogTextField { 66 | if #available(iOS 11.0, *) { 67 | $0.textContentType = .telephoneNumber 68 | } 69 | $0.keyboardType = .phonePad 70 | $0.autocapitalizationType = .none 71 | $0.autocorrectionType = .no 72 | $0.spellCheckingType = .no 73 | configuration($0) 74 | } 75 | } 76 | } 77 | 78 | public struct DialogAction { 79 | public let title: String 80 | public let style: DialogActionStyle 81 | public let onNext: ([UITextField]) -> Observable 82 | public let isPreferred: Bool 83 | 84 | public init(title: String, style: DialogActionStyle, onNext: @escaping ([UITextField]) -> Observable, isPreferred: Bool = false) { 85 | self.title = title 86 | self.style = style 87 | self.onNext = onNext 88 | self.isPreferred = isPreferred 89 | } 90 | 91 | public func withStyle(_ newStyle: DialogActionStyle) -> DialogAction { 92 | DialogAction( 93 | title: title, 94 | style: newStyle, 95 | onNext: onNext, 96 | isPreferred: isPreferred 97 | ) 98 | } 99 | 100 | // MARK: - flatMap with TextFields 101 | 102 | public static func `default`(title: String, flatMap observable: @escaping ([UITextField]) -> O) -> DialogAction where O.Element == T { 103 | DialogAction(title: title, style: .default, onNext: { observable($0).asObservable() }) 104 | } 105 | 106 | public static func cancel(title: String, flatMap observable: @escaping ([UITextField]) -> O) -> DialogAction where O.Element == T { 107 | DialogAction(title: title, style: .cancel, onNext: { observable($0).asObservable() }) 108 | } 109 | 110 | public static func destructive(title: String, flatMap observable: @escaping ([UITextField]) -> O) -> DialogAction where O.Element == T { 111 | DialogAction(title: title, style: .destructive, onNext: { observable($0).asObservable() }) 112 | } 113 | 114 | // MARK: - flatMapTo without TextFields 115 | 116 | public static func `default`(title: String, flatMapTo observable: O) -> DialogAction where O.Element == T { 117 | DialogAction(title: title, style: .default, onNext: { _ in observable.asObservable() }) 118 | } 119 | 120 | public static func cancel(title: String, flatMapTo observable: O) -> DialogAction where O.Element == T { 121 | DialogAction(title: title, style: .cancel, onNext: { _ in observable.asObservable() }) 122 | } 123 | 124 | public static func destructive(title: String, flatMapTo observable: O) -> DialogAction where O.Element == T { 125 | DialogAction(title: title, style: .destructive, onNext: { _ in observable.asObservable() }) 126 | } 127 | 128 | // MARK: - map with TextFields 129 | 130 | public static func `default`(title: String, map value: @escaping ([UITextField]) -> T) -> DialogAction { 131 | .default(title: title, flatMap: { Observable.just(value($0)) }) 132 | } 133 | 134 | public static func cancel(title: String, map value: @escaping ([UITextField]) -> T) -> DialogAction { 135 | .cancel(title: title, flatMap: { Observable.just(value($0)) }) 136 | } 137 | 138 | public static func destructive(title: String, map value: @escaping ([UITextField]) -> T) -> DialogAction { 139 | .destructive(title: title, flatMap: { Observable.just(value($0)) }) 140 | } 141 | 142 | // MARK: - mapTo without TextFields 143 | 144 | public static func `default`(title: String, mapTo value: T) -> DialogAction { 145 | .default(title: title, flatMapTo: Observable.just(value)) 146 | } 147 | 148 | public static func cancel(title: String, mapTo value: T) -> DialogAction { 149 | .cancel(title: title, flatMapTo: Observable.just(value)) 150 | } 151 | 152 | public static func destructive(title: String, mapTo value: T) -> DialogAction { 153 | .destructive(title: title, flatMapTo: Observable.just(value)) 154 | } 155 | 156 | // MARK: - Throw 157 | 158 | public static func `default`(title: String, throw error: Error) -> DialogAction { 159 | .default(title: title, flatMapTo: Observable.error(error)) 160 | } 161 | 162 | public static func cancel(title: String, throw error: Error) -> DialogAction { 163 | .cancel(title: title, flatMapTo: Observable.error(error)) 164 | } 165 | 166 | public static func destructive(title: String, throw error: Error) -> DialogAction { 167 | .destructive(title: title, flatMapTo: Observable.error(error)) 168 | } 169 | 170 | // MARK: - No Values 171 | 172 | public static func `default`(title: String) -> DialogAction { 173 | .default(title: title, flatMapTo: Observable.empty()) 174 | } 175 | 176 | public static func cancel(title: String) -> DialogAction { 177 | .cancel(title: title, flatMapTo: Observable.empty()) 178 | } 179 | 180 | public static func destructive(title: String) -> DialogAction { 181 | .destructive(title: title, flatMapTo: Observable.empty()) 182 | } 183 | 184 | // MARK: - Higher Order Actions 185 | 186 | public static func preferred(_ action: DialogAction) -> DialogAction { 187 | DialogAction( 188 | title: action.title, 189 | style: action.style, 190 | onNext: action.onNext, 191 | isPreferred: true 192 | ) 193 | } 194 | } 195 | 196 | extension DialogAction where T == Void { 197 | // MARK: - flatMap with TextFields 198 | 199 | public static func `default`(title: String, flatMap observable: @escaping ([UITextField]) -> O) -> DialogAction { 200 | DialogAction(title: title, style: .default, onNext: { observable($0).asObservable().map { _ in () } }) 201 | } 202 | 203 | public static func cancel(title: String, flatMap observable: @escaping ([UITextField]) -> O) -> DialogAction { 204 | DialogAction(title: title, style: .cancel, onNext: { observable($0).asObservable().map { _ in () } }) 205 | } 206 | 207 | public static func destructive(title: String, flatMap observable: @escaping ([UITextField]) -> O) -> DialogAction { 208 | DialogAction(title: title, style: .destructive, onNext: { observable($0).asObservable().map { _ in () } }) 209 | } 210 | 211 | // MARK: - flatMapTo without TextFields 212 | 213 | public static func `default`(title: String, flatMapTo observable: O) -> DialogAction { 214 | DialogAction(title: title, style: .default, onNext: { _ in observable.asObservable().map { _ in () } }) 215 | } 216 | 217 | public static func cancel(title: String, flatMapTo observable: O) -> DialogAction { 218 | DialogAction(title: title, style: .cancel, onNext: { _ in observable.asObservable().map { _ in () } }) 219 | } 220 | 221 | public static func destructive(title: String, flatMapTo observable: O) -> DialogAction { 222 | DialogAction(title: title, style: .destructive, onNext: { _ in observable.asObservable().map { _ in () } }) 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /Sources/Dialogs/UIAlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertController.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 07/02/2021. 6 | // 7 | 8 | import UIKit 9 | import RxSwift 10 | 11 | extension DialogActionStyle { 12 | fileprivate var alertActionStyle: UIAlertAction.Style { 13 | switch self { 14 | case .default: 15 | return .default 16 | case .cancel: 17 | return .cancel 18 | case .destructive: 19 | return .destructive 20 | } 21 | } 22 | } 23 | 24 | extension DialogAction { 25 | fileprivate func makeAlertAction(observer: AnyObserver>, textFields: @escaping () -> [UITextField]) -> UIAlertAction { 26 | UIAlertAction(title: title, style: style.alertActionStyle) { _ in 27 | observer.onNext(self.onNext(textFields())) 28 | observer.onCompleted() 29 | } 30 | } 31 | } 32 | 33 | extension Dialog { 34 | fileprivate func makeAlertController(style: DialogStyle, observer: AnyObserver>) -> UIAlertController { 35 | let controller: UIAlertController 36 | 37 | switch style { 38 | case .alert: 39 | controller = UIAlertController(title: title, message: message, preferredStyle: .alert) 40 | case .actionSheet(let source): 41 | controller = UIAlertController(title: title, message: message, preferredStyle: .actionSheet) 42 | switch source { 43 | case .view(let view, let rect): 44 | controller.popoverPresentationController?.sourceRect = rect 45 | controller.popoverPresentationController?.sourceView = view 46 | case .barButtonItem(let item): 47 | controller.popoverPresentationController?.barButtonItem = item 48 | } 49 | } 50 | 51 | if case .alert = style { 52 | for textField in textFields { 53 | controller.addTextField(configurationHandler: textField.configuration) 54 | } 55 | } 56 | 57 | for action in actions { 58 | let alertAction = action.makeAlertAction(observer: observer) { [unowned controller] in 59 | controller.textFields ?? [] 60 | } 61 | controller.addAction(alertAction) 62 | if action.isPreferred { 63 | controller.preferredAction = alertAction 64 | } 65 | } 66 | 67 | return controller 68 | } 69 | } 70 | 71 | public enum DialogSource { 72 | case view(UIView, rect: CGRect) 73 | case barButtonItem(UIBarButtonItem) 74 | public static func bounds(_ view: UIView) -> DialogSource { 75 | .view(view, rect: view.bounds) 76 | } 77 | } 78 | 79 | public enum DialogStyle { 80 | case alert 81 | case actionSheet(source: DialogSource) 82 | } 83 | 84 | extension RxModal { 85 | public static func alert( 86 | _ type: T.Type = T.self, 87 | presenter: Presenter = .keyWindow, 88 | title: String? = nil, 89 | message: String? = nil, 90 | textFields: [DialogTextField] = [], 91 | actions: [DialogAction] 92 | ) -> Observable { 93 | present(.alert, presenter: presenter, title: title, message: message, textFields: textFields, actions: actions) 94 | } 95 | 96 | public static func actionSheet( 97 | _ type: T.Type = T.self, 98 | presenter: Presenter = .keyWindow, 99 | source: DialogSource, 100 | title: String? = nil, 101 | message: String? = nil, 102 | actions: [DialogAction] 103 | ) -> Observable { 104 | present(.actionSheet(source: source), presenter: presenter, title: title, message: message, actions: actions) 105 | } 106 | 107 | public static func present( 108 | _ style: DialogStyle, 109 | type: T.Type = T.self, 110 | presenter: Presenter = .keyWindow, 111 | title: String? = nil, 112 | message: String? = nil, 113 | textFields: [DialogTextField] = [], 114 | actions: [DialogAction] 115 | ) -> Observable { 116 | let dialog = Dialog(title: title, message: message, textFields: textFields, actions: actions) 117 | return present( dialog, style: style, presenter: presenter) 118 | } 119 | 120 | public static func present( 121 | _ dialog: Dialog, 122 | style: DialogStyle, 123 | presenter: Presenter = .keyWindow 124 | ) -> Observable { 125 | UIAlertControllerCoordinator.present(using: presenter) { coordinator in 126 | dialog.makeAlertController(style: style, observer: coordinator.result.asObserver()) 127 | } sequence: { coordinator in 128 | coordinator.result.merge() 129 | } 130 | } 131 | } 132 | 133 | private class UIAlertControllerCoordinator: RxModalCoordinator { 134 | required init() {} 135 | let result = PublishSubject>() 136 | } 137 | -------------------------------------------------------------------------------- /Sources/Internals.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Internals.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 05/02/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | infix operator ..: MultiplicationPrecedence 11 | 12 | @discardableResult 13 | internal func .. (object: T, configuration: (inout T) -> Void) -> T { 14 | var copy = object 15 | configuration(©) 16 | return copy 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Other/ASWebAuthenticationSession.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASWebAuthenticationSession.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 10/02/2021. 6 | // 7 | 8 | import UIKit 9 | import AuthenticationServices 10 | import RxSwift 11 | 12 | extension RxModal { 13 | 14 | @available(iOS 12.0, *) 15 | public static func webAuthenticationSession( 16 | presenter: Presenter = .keyWindow, 17 | url: URL, 18 | callbackURLScheme: String, 19 | configuration: @escaping (ASWebAuthenticationSession) -> Void = { _ in } 20 | ) -> Single { 21 | .using { 22 | try RxWebAuthenticationSessionCoordinator( 23 | presenter: presenter, 24 | url: url, 25 | callbackURLScheme: callbackURLScheme, 26 | configuration: configuration 27 | ) 28 | } primitiveSequenceFactory: { coordinator in 29 | coordinator.result.asSingle() 30 | } 31 | } 32 | } 33 | 34 | @available(iOS 12.0, *) 35 | private class RxWebAuthenticationSessionCoordinator: NSObject, ASWebAuthenticationPresentationContextProviding, Disposable { 36 | var window: UIWindow! 37 | let session: ASWebAuthenticationSession 38 | let result: PublishSubject 39 | 40 | init( 41 | presenter: Presenter, 42 | url: URL, 43 | callbackURLScheme: String, 44 | configuration: @escaping (ASWebAuthenticationSession) -> Void 45 | ) throws { 46 | let result = PublishSubject() 47 | let session = ASWebAuthenticationSession( 48 | url: url, 49 | callbackURLScheme: callbackURLScheme, 50 | completionHandler: { url, error in 51 | if let error = error { 52 | return result.onError(error) 53 | } 54 | guard let url = url else { 55 | return result.onError(RxModalError.unsupported) 56 | } 57 | result.onNext(url) 58 | result.onCompleted() 59 | } 60 | ) 61 | 62 | configuration(session) 63 | 64 | self.result = result 65 | self.session = session 66 | 67 | super.init() 68 | 69 | if #available(iOS 13.0, *) { 70 | guard let window = presenter()?.viewIfLoaded?.window else { 71 | throw RxModalError.missingPresenter 72 | } 73 | self.window = window 74 | self.session.presentationContextProvider = self 75 | } 76 | 77 | self.session.start() 78 | } 79 | 80 | func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { 81 | return window 82 | } 83 | 84 | public func dispose() { 85 | session.cancel() 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Sources/Pickers/MPMediaPickerController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MPMediaPickerController.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 03/02/2021. 6 | // 7 | 8 | #if canImport(MediaPlayer) 9 | import UIKit 10 | import MediaPlayer 11 | import RxSwift 12 | import RxCocoa 13 | 14 | @available(iOS 9.3, *) 15 | extension RxModal { 16 | public static var mediaLibraryAuthorizationStatus: Single { 17 | Single.deferred { 18 | let status = MPMediaLibrary.authorizationStatus() 19 | guard case .notDetermined = status else { 20 | return .just(status) 21 | } 22 | return Single.create { observer in 23 | MPMediaLibrary.requestAuthorization { status in 24 | observer(.success(status)) 25 | } 26 | return Disposables.create() 27 | } 28 | } 29 | } 30 | 31 | /** 32 | Media Picker 33 | 34 | Requires `NSAppleMusicUsageDescription` key in App's `Info.plist` 35 | */ 36 | public static func mediaPicker(presenter: Presenter = .keyWindow, configuration: @escaping (MPMediaPickerController) -> Void = { _ in }) -> Single { 37 | MPMediaPickerControllerCoordinator.present(using: presenter) { delegate in 38 | MPMediaPickerController(mediaTypes: .any)..{ 39 | configuration($0) 40 | $0.delegate = delegate 41 | } 42 | } sequence: { 43 | $0.subject.asSingle() 44 | } 45 | .require(mediaLibraryAuthorizationStatus, in: .authorized, .restricted) 46 | } 47 | } 48 | 49 | private class MPMediaPickerControllerCoordinator: RxModalCoordinator, MPMediaPickerControllerDelegate { 50 | required init() {} 51 | 52 | let subject = PublishSubject() 53 | 54 | func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) { 55 | subject.onNext(mediaItemCollection) 56 | subject.onCompleted() 57 | } 58 | 59 | func mediaPickerDidCancel(_ mediaPicker: MPMediaPickerController) { 60 | subject.onNext(MPMediaItemCollection(items: [])) 61 | subject.onCompleted() 62 | } 63 | } 64 | #endif 65 | -------------------------------------------------------------------------------- /Sources/Pickers/PHPickerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PHPickerViewController.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 03/02/2021. 6 | // 7 | 8 | #if canImport(PhotosUI) 9 | import UIKit 10 | import PhotosUI 11 | import RxSwift 12 | import RxCocoa 13 | 14 | extension RxModal { 15 | @available(iOS 14, *) 16 | public static func photoPicker(presenter: Presenter = .keyWindow, configuration: @escaping (inout PHPickerConfiguration) -> Void = { _ in }) -> Single<[PHPickerResult]> { 17 | PHPickerViewControllerCoordinator.present(using: presenter) { delegate in 18 | PHPickerViewController(configuration: PHPickerConfiguration() .. configuration)..{ 19 | $0.delegate = delegate 20 | } 21 | } sequence: { 22 | $0.pickerResults.asSingle() 23 | } 24 | } 25 | } 26 | 27 | @available(iOS 14, *) 28 | private class PHPickerViewControllerCoordinator: RxModalCoordinator, PHPickerViewControllerDelegate { 29 | required init() {} 30 | 31 | let pickerResults = PublishSubject<[PHPickerResult]>() 32 | 33 | func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { 34 | pickerResults.onNext(results) 35 | pickerResults.onCompleted() 36 | } 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /Sources/Presenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Presenter.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 05/02/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | public struct Presenter { 11 | private let get: () -> UIViewController? 12 | 13 | public static let keyWindow = Presenter { 14 | if #available(iOS 13.0, *) { 15 | let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene } 16 | assert(scenes.count <= 1, "Several connected scenes. You probably want to use a more specific presenter.") 17 | let windows = scenes.first?.windows.filter { $0.isKeyWindow } ?? [] 18 | assert(windows.count <= 1, "Several windows found in the scene. You probably want to use a more specific presenter.") 19 | return windows.first?.rootViewController 20 | } else { 21 | return UIApplication.shared.keyWindow?.rootViewController 22 | } 23 | } 24 | 25 | public static func window(_ window: UIWindow?) -> Presenter { 26 | Presenter { [weak window] in 27 | window?.rootViewController 28 | } 29 | } 30 | 31 | public static func viewController(_ viewController: UIViewController?) -> Presenter { 32 | Presenter { [weak viewController] in 33 | viewController 34 | } 35 | } 36 | 37 | public static func view(_ view: UIView?) -> Presenter { 38 | Presenter { [weak view] in 39 | view?.window?.rootViewController 40 | } 41 | } 42 | 43 | @available(iOS 13.0, *) 44 | public static func scene(_ scene: UIWindowScene) -> Presenter { 45 | Presenter { [weak scene] in 46 | guard let scene = scene else { return nil } 47 | assert(scene.windows.count <= 1, "Several windows found in the scene. You probably want to use a more specific presenter.") 48 | return scene.windows.first?.rootViewController 49 | } 50 | } 51 | 52 | public func callAsFunction() -> UIViewController? { 53 | get() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Rx+AuthorizationStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Rx+AuthorizationStatus.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 05/02/2021. 6 | // 7 | 8 | import UIKit 9 | import RxSwift 10 | 11 | // MARK: - Completable 12 | 13 | extension PrimitiveSequence where Trait == CompletableTrait, Element == Never { 14 | public func require(_ status: Single, in expectedStatus: T...) -> Completable { 15 | status.flatMapCompletable { status in 16 | guard expectedStatus.contains(status) else { 17 | return .error(RxModalError.authorizationDenied(T.self)) 18 | } 19 | return self 20 | } 21 | } 22 | } 23 | 24 | // MARK: - Single 25 | 26 | extension PrimitiveSequence where Trait == SingleTrait { 27 | public func require(_ status: Single, in expectedStatus: T...) -> Single { 28 | status.flatMap { status -> Single in 29 | guard expectedStatus.contains(status) else { 30 | return .error(RxModalError.authorizationDenied(T.self)) 31 | } 32 | return self 33 | } 34 | } 35 | } 36 | 37 | // MARK: - Maybe 38 | 39 | extension PrimitiveSequence where Trait == MaybeTrait { 40 | public func require(_ status: Single, in expectedStatus: T...) -> Maybe { 41 | status.flatMapMaybe { status in 42 | guard expectedStatus.contains(status) else { 43 | return .error(RxModalError.authorizationDenied(T.self)) 44 | } 45 | return self 46 | } 47 | } 48 | } 49 | 50 | // MARK: - Observable 51 | 52 | extension Observable { 53 | public func require(_ status: Single, in expectedStatus: T...) -> Observable { 54 | status.asObservable().flatMap { status -> Observable in 55 | guard expectedStatus.contains(status) else { 56 | return .error(RxModalError.authorizationDenied(T.self)) 57 | } 58 | return self 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/RxModal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxModal.swift 3 | // RxSwiftCommunity 4 | // 5 | // Created by Jérôme Alves on 03/02/2021. 6 | // Copyright © 2021 RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | 12 | public enum RxModal { 13 | 14 | } 15 | 16 | public enum RxModalError: Error { 17 | case missingPresenter 18 | case unsupported 19 | case authorizationDenied(Any.Type) 20 | } 21 | -------------------------------------------------------------------------------- /Sources/RxModalCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxModalCoordinator.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 05/02/2021. 6 | // 7 | 8 | import UIKit 9 | import RxSwift 10 | 11 | open class RxModalCoordinator: NSObject, Disposable { 12 | private var _viewController: ViewController? 13 | public var viewController: ViewController { 14 | _viewController! 15 | } 16 | 17 | required public override init() {} 18 | 19 | open func present(_ controller: @autoclosure () -> ViewController, with presenter: Presenter) throws { 20 | guard let presenter = presenter() else { 21 | throw RxModalError.missingPresenter 22 | } 23 | _viewController = controller() 24 | presenter.present(viewController, animated: true, completion: nil) 25 | } 26 | 27 | open func dispose() { 28 | guard viewController.isBeingDismissed == false else { 29 | return 30 | } 31 | viewController.presentingViewController?.dismiss(animated: true, completion: nil) 32 | } 33 | } 34 | 35 | extension NSObjectProtocol { 36 | 37 | //MARK: - Completable 38 | 39 | public static func present( 40 | using presenter: Presenter = .keyWindow, 41 | controllerFactory: @escaping (Self) -> ViewController, 42 | sequence: @escaping (Self) -> Completable 43 | ) -> Completable where Self: RxModalCoordinator { 44 | Completable.using { 45 | let coordinator = Self.init() 46 | try coordinator.present(controllerFactory(coordinator), with: presenter) 47 | return coordinator 48 | } primitiveSequenceFactory: { coordinator in 49 | sequence(coordinator) 50 | } 51 | .subscribe(on: MainScheduler.instance) 52 | 53 | } 54 | 55 | //MARK: - Single 56 | 57 | public static func present( 58 | using presenter: Presenter = .keyWindow, 59 | controllerFactory: @escaping (Self) -> ViewController, 60 | sequence: @escaping (Self) -> Single 61 | ) -> Single where Self: RxModalCoordinator { 62 | Single.using { 63 | let coordinator = Self.init() 64 | try coordinator.present(controllerFactory(coordinator), with: presenter) 65 | return coordinator 66 | } primitiveSequenceFactory: { coordinator in 67 | sequence(coordinator) 68 | } 69 | .subscribe(on: MainScheduler.instance) 70 | } 71 | 72 | //MARK: - Maybe 73 | 74 | public static func present( 75 | using presenter: Presenter = .keyWindow, 76 | controllerFactory: @escaping (Self) -> ViewController, 77 | sequence: @escaping (Self) -> Maybe 78 | ) -> Maybe where Self: RxModalCoordinator { 79 | Maybe.using { 80 | let coordinator = Self.init() 81 | try coordinator.present(controllerFactory(coordinator), with: presenter) 82 | return coordinator 83 | } primitiveSequenceFactory: { coordinator in 84 | sequence(coordinator) 85 | } 86 | .subscribe(on: MainScheduler.instance) 87 | } 88 | 89 | //MARK: - Observable 90 | 91 | public static func present( 92 | using presenter: Presenter = .keyWindow, 93 | controllerFactory: @escaping (Self) -> ViewController, 94 | sequence: @escaping (Self) -> Observable 95 | ) -> Observable where Self: RxModalCoordinator { 96 | Observable.using { 97 | let coordinator = Self.init() 98 | try coordinator.present(controllerFactory(coordinator), with: presenter) 99 | return coordinator 100 | } observableFactory: { coordinator in 101 | sequence(coordinator) 102 | } 103 | .subscribe(on: MainScheduler.instance) 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /Sources/RxModalDescription.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxModalDescription.swift 3 | // RxModal 4 | // 5 | // Created by Jérôme Alves on 06/02/2021. 6 | // 7 | 8 | import UIKit 9 | 10 | public protocol RxModalCustomStringConvertible { 11 | var rxModalDescription: String { get } 12 | } 13 | 14 | extension String { 15 | public init(rxModalDescribing value: Any) { 16 | if let value = value as? RxModalCustomStringConvertible { 17 | self = value.rxModalDescription 18 | } else { 19 | self = String(describing: value) 20 | } 21 | } 22 | } 23 | 24 | #if canImport(MessageUI) 25 | import MessageUI 26 | extension MFMailComposeResult: RxModalCustomStringConvertible { 27 | public var rxModalDescription: String { 28 | switch self { 29 | case .cancelled: 30 | return "cancelled" 31 | case .saved: 32 | return "saved" 33 | case .sent: 34 | return "sent" 35 | case .failed: 36 | return "failed" 37 | @unknown default: 38 | return "@unknown" 39 | } 40 | } 41 | } 42 | 43 | extension MessageComposeResult: RxModalCustomStringConvertible { 44 | public var rxModalDescription: String { 45 | switch self { 46 | case .cancelled: 47 | return "cancelled" 48 | case .sent: 49 | return "sent" 50 | case .failed: 51 | return "failed" 52 | @unknown default: 53 | return "@unknown" 54 | } 55 | } 56 | } 57 | #endif 58 | 59 | #if canImport(MediaPlayer) 60 | import MediaPlayer 61 | @available(iOS 9.3, *) 62 | extension MPMediaLibraryAuthorizationStatus: RxModalCustomStringConvertible { 63 | public var rxModalDescription: String { 64 | switch self { 65 | case .notDetermined: 66 | return "notDetermined" 67 | case .denied: 68 | return "denied" 69 | case .restricted: 70 | return "restricted" 71 | case .authorized: 72 | return "authorized" 73 | @unknown default: 74 | return "@unknown" 75 | } 76 | } 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import RxModalTests 3 | 4 | XCTMain([ 5 | testCase(RxModalTests.allTests), 6 | ]) 7 | -------------------------------------------------------------------------------- /Tests/RxModalTests/RxModalTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxModalTests.swift 3 | // RxSwiftCommunity 4 | // 5 | // Created by Jérôme Alves on 03/02/2021. 6 | // Copyright © 2021 RxSwiftCommunity. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import RxModal 12 | 13 | class RxModalTests: XCTestCase { 14 | func testExample() { 15 | // This is an example of a functional test case. 16 | // Use XCTAssert and related functions to verify your tests produce the correct results. 17 | //// XCTAssertEqual(RxModal().text, "Hello, World!") 18 | } 19 | 20 | static var allTests = [ 21 | ("testExample", testExample), 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /assets/RxModal_Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/assets/RxModal_Demo.gif -------------------------------------------------------------------------------- /assets/RxModal_Icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxModal/ff3e03221a827a2a87559a240acdb27d6d557453/assets/RxModal_Icons.png --------------------------------------------------------------------------------