An open API service indexing awesome lists of open source software.

https://github.com/rxswiftcommunity/rxmodal

Subscribe to your modal flows
https://github.com/rxswiftcommunity/rxmodal

modals rxcocoa rxswift uikit

Last synced: about 1 year ago
JSON representation

Subscribe to your modal flows

Awesome Lists containing this project

README

          


RxModal Icons




# RxModal

**RxModal** enforces the simple idea that a modal flow can be considered as a simple asynchroneous event:
- the view controller is presented on subscribe
- the user do what they want to do in the modal view
- the view controller is dismissed on dispose and eventually emit a value or an error

# Usage


Here's an example
In Action



let mailComposer = RxModal.mailComposer {
$0.setToRecipients([
"rxmodal@rxswiftcommunity.org"
])
$0.setMessageBody(
"Hello World!",
isHTML: false
)
}
let messageComposer = RxModal.messageComposer {
$0.recipients = ["0639981337"]
$0.body = "Hello World!"
}
contactUsButton
.rx.tap
.flatMapFirst { [unowned contactUsButton] in
RxModal.actionSheet(
source: .bounds(contactUsButton),
actions: [
.default(
title: "Mail",
flatMapTo: mailComposer
),
.default(
title: "Message",
flatMapTo: messageComposer
),
.cancel(title: "Cancel")
])
}
.subscribe()
.disposed(by: disposeBag)





# Supported Modals

```swift
// MFMailComposeViewController
RxModal.mailComposer() -> Single

// MFMessageComposeViewController
RxModal.messageComposer() -> Single

// MPMediaPickerController
RxModal.mediaPicker() -> Single

// PHPickerViewController
RxModal.photoPicker() -> Single<[PHPickerResult]>

// ASWebAuthenticationSession
RxModal.webAuthenticationSession(url:callbackURLScheme:) -> Single

// UIAlertController
RxModal.alert(title:message:textFields:actions:) -> Observable
RxModal.actionSheet(source:title:message:actions:) -> Observable
```

### Presenter

All these functions also include a `presenter: Presenter` argument that allows you to choose where the modal will be presented.
Presenters are just lazy `UIViewController` getters:
```swift
.viewController(_:) -> $0
.view(_:) -> $0.window?.rootViewController
.window(_:) -> $0.rootViewController
.scene(_:) -> $0.windows.first?.rootViewController
.keyWindow -> UIApplication.shared.keyWindow?.rootViewController
```
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.

### Configuration

These functions also include a configuration closure : `(ViewController) -> Void` that will let you configure the view controller before presentation.

If a modal requires some parameters at `init` time, they will be part of the `RxModal` function (ex: `ASWebAuthenticationSession`, `UIAlertController`).

### Preconditions

Some RxModals perform precondition checks before presenting the modal and emit a `RxModalError.unsupported` if they aren't fulfilled:
- `RxModal.mailComposer()` → `MFMailComposeViewController.canSendMail()`
- `RxModal.messageComposer()` → `MFMessageComposeViewController.canSendText()`

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:
- `RxModal.mediaPicker()` → `MPMediaLibrary.authorizationStatus()`

### Dialogs

`RxModal.alert()` and `RxModal.actionSheet()` allows you to define actions that are converted to a new Observable stream, a value, or an error:
```swift
DialogAction.default(title:flatMapTo: Observable)
DialogAction.default(title:mapTo: T) // == flatMapTo: Observable.just(T)
DialogAction.default(title:throw: Error) // == flatMapTo: Observable.error(Error)
DialogAction.default(title:) // == flatMapTo: Observable.empty()
```

`RxModal.alert()` also let you configure alert text fields:
```swift
RxModal.alert(
title: "Sign in",
message: "Please sign in using your credentials",
textFields: [
DialogTextField.email { $0.placeholder = "e-mail" },
DialogTextField.password { $0.placeholder = "password" }
],
actions: [
.cancel(title: "Cancel"),
.default(title: "Sign In") { textFields in
Credentials(
email: textFields[0].text ?? "",
password: textFields[1].text ?? ""
)
},
]
)
```

# Extending RxModal

You can easily extend RxModal with your own controllers / modal flows.

If your controller is already returning its output using Rx, it's easy:
```swift
class MyModalViewController: UIViewController {
let myResult = PublishSubject()
// ...
}

extension RxModal {
func myModal(
presenter: Presenter = .keyWindow,
configuration: @escaping (MyModalViewController) -> Void
) -> Single {
RxModalCoordinator.present(using: presenter) { _ in
let modal = MyModalViewController()
configuration(modal)
return modal
} sequence: {
$0.viewController.myResult.asSingle()
}
}
}
```

If your controller is rather using a traditional `delegate` approach, you'll need to subclass `RxModalCoordinator`:

```swift
protocol MyModalViewControllerDelegate: AnyObject {
func myModal(_ myModal: MyModalViewController, didFinishWith result: MyResult)
func myModal(_ myModal: MyModalViewController, didFinishWithError error: Error)
}

class MyModalViewController: UIViewController {
weak var delegate: MyModalViewControllerDelegate?
// ...
}

extension RxModal {
func myModal(
presenter: Presenter = .keyWindow,
configuration: @escaping (MyModalViewController) -> Void
) -> Single {

MyModalViewControllerCoordinator.present(using: presenter) { coordinator in
let modal = MyModalViewController()
modal.delegate = coordinator
configuration(modal)
return modal
} sequence: {
$0.myResult.asSingle()
}

}
}

final class MyModalViewControllerCoordinator: RxModalCoordinator, MyModalViewControllerDelegate {
required init(){}

let myResult = PublishSubject()

func myModal(_ myModal: MyModalViewController, didFinishWith result: MyResult) {
myResult.onNext(result)
myResult.onCompleted()
}

func myModal(_ myModal: MyModalViewController, didFinishWithError error: Error) {
myResult.onError(error)
}

}
```

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.

# Author

[Jérôme Alves](https://twitter.com/jegnux)

# License

**RxModal** is available under the MIT license. See the [LICENSE](LICENSE) file for more info.