Note: If you're using Modals and set up a ModalHostContainerViewController at the root of your app, you do not need to set up a ModalHostContainer.
WorkflowModals uses a container screen to host presented modals. In order to present modals within your application, you must install a ModalHostContainer at the root of your workflow hierarchy. For example, in your scene delegate you could map your workflow's rendering:
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
// Set up the modal host
let rootWorkflow = MyRootWorkflow()
.ignoringOutput()
.mapRendering(ModalHostContainer.init)
window.rootViewController = WorkflowHostingController(workflow: rootWorkflow)
self.window = window
window.makeKeyAndVisible()
}WorkflowModals uses the same presentation style types as Modals - for an example style, see the vanilla UIKit usage guidelines.
Now that we have a host and a modal style, render a modal in our workflow. The framework provides a ModalContainer screen, which allows you to render a "base" screen and an array of modals to present over that screen. ModalContainer is generic over two parameters: the base screen type, and the screen type for the modal (which can be AnyScreen if modals of different screen types are being rendered). Each modal can have its own presentation style, which is not tied to the rendering type in any way - instead, create a Modal with your screen, presentation style, and identifying key, and pass those modals into the ModalContainer:
struct ModalWorkflow: Workflow {
typealias Rendering = ModalContainer<ModalScreen, ModalScreen>
struct State {
var showModal: Bool
}
enum Action: WorkflowAction {
typealias WorkflowType = ModalWorkflow
case showModal
case dismissModal
func apply(toState state: inout ModalWorkflow.State) -> ModalWorkflow.Output? {
switch self {
case .showModal:
state.showModal = true
case .dismissModal:
state.showModal = false
}
return nil
}
}
func makeInitialState() -> State {
State(showModal: false)
}
func render(state: State, context: RenderContext<Self>) -> Rendering {
let sink = context.makeSink(of: Action.self)
let baseScreen = ModalScreen(buttonText: "Present Modal") {
sink.send(.showModal)
}
return baseScreen.presenting {
if state.showingModal {
ModalScreen(buttonText: "Dismiss Modal") {
sink.send(.dismissModal)
}.modal(key: "modal", style: MyModalStyle())
}
}
}
}
struct ModalScreen: Screen {
var buttonText: String
var onTap: () -> Void
// screen implementation here
}