From 3fdc927c76166cdf1aea6736487c25f58733a829 Mon Sep 17 00:00:00 2001 From: Dhaval Shreyas Date: Tue, 3 Mar 2020 08:49:06 -0800 Subject: [PATCH] Update docs to reflect ViewRegistry removal: --- .../building-a-view-controller-from-screen.md | 4 +- docs/tutorial/using-a-workflow-for-ui.md | 33 +++--------- swift/Samples/Tutorial/Tutorial1.md | 24 ++++----- swift/Samples/Tutorial/Tutorial2.md | 52 +++++++------------ swift/Samples/Tutorial/Tutorial3.md | 43 +++++---------- .../___FILEBASENAME___Screen.swift | 17 +++--- 6 files changed, 55 insertions(+), 118 deletions(-) diff --git a/docs/tutorial/building-a-view-controller-from-screen.md b/docs/tutorial/building-a-view-controller-from-screen.md index 92cd10191..766a5b601 100644 --- a/docs/tutorial/building-a-view-controller-from-screen.md +++ b/docs/tutorial/building-a-view-controller-from-screen.md @@ -18,9 +18,9 @@ class DemoScreenViewController: ScreenViewController { private let button: UIButton - required init(screen: DemoScreen, viewRegistry: ViewRegistry) { + required init(screen: DemoScreen) { button = UIButton() - super.init(screen: screen, viewRegistry: viewRegistry) + super.init(screen: screen) update(screen: screen) } diff --git a/docs/tutorial/using-a-workflow-for-ui.md b/docs/tutorial/using-a-workflow-for-ui.md index 9764b912b..ea0b2ef0e 100644 --- a/docs/tutorial/using-a-workflow-for-ui.md +++ b/docs/tutorial/using-a-workflow-for-ui.md @@ -14,16 +14,12 @@ public final class ContainerViewController: UIViewController /// Emits output events from the bound workflow. public let output: Signal - public convenience init(workflow: W, viewRegistry: ViewRegistry) where W.Rendering == ScreenType, W.Output == Output + public convenience init(workflow: W) where W.Rendering == ScreenType, W.Output == Output } ``` -The first initializer argument is the workflow that will drive your application. - -The second initializer argument is the view registry. The view registry acts as a mapping between -the view models (`Screen`s) that your workflow emits and the concrete UI implementations that should -be used to display them. +The initializer argument is the workflow that will drive your application. ```swift import UIKit @@ -38,11 +34,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let window = UIWindow(frame: UIScreen.main.bounds) - var viewRegistry = ViewRegistry() - let container = ContainerViewController( - workflow: DemoWorkflow(), - viewRegistry: viewRegistry) + workflow: DemoWorkflow() + ) window.rootViewController = container self.window = window @@ -53,21 +47,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ``` -Your project should compile at this point. It will crash as soon as the workflow emits a screen, -however, because we have not registered any UI implementations with the view registry. Let's fix -that: - -```swift -var viewRegistry = ViewRegistry() - -// Register the DemoScreenViewController to be responsible for DemoScreen. -viewRegistry.register(screenViewControllerType: DemoScreenViewController.self) - -let container = ContainerViewController( - workflow: DemoWorkflow(), - viewRegistry: viewRegistry) -``` - Now, when the `ContainerViewController` is shown, it will start the workflow and `render` will be -called returning the `DemoScreen`. The container will use the view registry to map the `DemoScreen` -to a `DemoScreenViewController` and add it to the view hierarchy to display. +called returning the `DemoScreen`. The container will use `viewControllerDescription` to build +a `DemoScreenViewController` and add it to the view hierarchy to display. diff --git a/swift/Samples/Tutorial/Tutorial1.md b/swift/Samples/Tutorial/Tutorial1.md index 19ca8e21f..b88cb75a8 100644 --- a/swift/Samples/Tutorial/Tutorial1.md +++ b/swift/Samples/Tutorial/Tutorial1.md @@ -43,6 +43,10 @@ struct WelcomeScreen: Screen { var onNameChanged: (String) -> Void /// Callback when the login button is tapped. var onLoginTapped: () -> Void + + var viewControllerDescription: ViewControllerDescription { + return WelcomeViewController.description(for: self) + } } ``` @@ -54,9 +58,9 @@ import TutorialViews final class WelcomeViewController: ScreenViewController { var welcomeView: WelcomeView - required init(screen: WelcomeScreen, viewRegistry: ViewRegistry) { + required init(screen: WelcomeScreen) { self.welcomeView = WelcomeView(frame: .zero) - super.init(screen: screen, viewRegistry: viewRegistry) + super.init(screen: screen) update(with: screen) } @@ -122,24 +126,16 @@ public final class TutorialContainerViewController: UIViewController { let containerViewController: UIViewController public init() { - // Create a view registry. This will allow the infrastructure to map `Screen` types to their respective view controller type. - var viewRegistry = ViewRegistry() - // Register the `WelcomeScreen` and view controller with the convenience method the template provided. - viewRegistry.registerWelcomeScreen() - - // Create a `ContainerViewController` with the `WelcomeWorkflow` as the root workflow, with the view registry we just created. + // Create a `ContainerViewController` with the `WelcomeWorkflow` as the root workflow. containerViewController = ContainerViewController( - workflow: WelcomeWorkflow(), - viewRegistry: viewRegistry) + workflow: WelcomeWorkflow() + ) super.init(nibName: nil, bundle: nil) } ``` -We did a few things here. -- We created a *view registry* that is used to map screen types to view controllers. The view registry is used by the container to determine what view controller to instantiate the first time we see a screen. -- We registered our `WelcomeScreen` with the view registry so we have that mapping. -- We created our `ContainerViewController` with the `WelcomeWorkflow` as the root. +Now, we've created our `ContainerViewController` with the `WelcomeWorkflow` as the root. We can finally run the app again! It will look the exact same as before, but now powered by our workflow. diff --git a/swift/Samples/Tutorial/Tutorial2.md b/swift/Samples/Tutorial/Tutorial2.md index dd5b80226..4115405f3 100644 --- a/swift/Samples/Tutorial/Tutorial2.md +++ b/swift/Samples/Tutorial/Tutorial2.md @@ -30,15 +30,19 @@ struct TodoListScreen: Screen { // It should also contain callbacks for any UI events, for example: // var onButtonTapped: () -> Void + + // It should also return viewControllerDescription property that + // describes the UIViewController that will be used for rendering + // the screen. } final class TodoListViewController: ScreenViewController { let todoListView: TodoListView - required init(screen: TodoListScreen, viewRegistry: ViewRegistry) { + required init(screen: TodoListScreen) { self.todoListView = TodoListView(frame: .zero) - super.init(screen: screen, viewRegistry: viewRegistry) + super.init(screen: screen) update(with: screen) } @@ -77,28 +81,21 @@ extension TodoListWorkflow { ### Showing the new screen and workflow -For now, let's just show this new screen instead of the login screen/workflow. Update the `TutorialContainerViewController` to register to the new screen and show the `TodoListWorkflow`: +For now, let's just show this new screen instead of the login screen/workflow. Update the `TutorialContainerViewController` to show the `TodoListWorkflow`: ```swift public final class TutorialContainerViewController: UIViewController { let containerViewController: UIViewController public init() { - // Create a view registry. This will allow the infrastructure to map `Screen` types to their respective view controller type. - var viewRegistry = ViewRegistry() - // Register the `WelcomeScreen` and view controller with the convenience method the template provided. - viewRegistry.registerWelcomeScreen() - // Register the `TodoListScreen` and view controller with the convenience method the template provided. - viewRegistry.registerTodoListScreen() - - // Create a `ContainerViewController` with the `WelcomeWorkflow` as the root workflow, with the view registry we just created. + // Create a `ContainerViewController` with the `WelcomeWorkflow` as the root workflow. // containerViewController = ContainerViewController( -// workflow: WelcomeWorkflow(), -// viewRegistry: viewRegistry) +// workflow: WelcomeWorkflow() +// ) // Show the TodoList Workflow instead: containerViewController = ContainerViewController( - workflow: TodoListWorkflow(), - viewRegistry: viewRegistry) + workflow: TodoListWorkflow() + ) super.init(nibName: nil, bundle: nil) } @@ -236,10 +233,10 @@ public final class TutorialContainerViewController: UIViewController { public init() { // ... - // Create a `ContainerViewController` with the `RootWorkflow` as the root workflow, with the view registry we just created. + // Create a `ContainerViewController` with the `RootWorkflow` as the root workflow. containerViewController = ContainerViewController( - workflow: RootWorkflow(), - viewRegistry: viewRegistry) + workflow: RootWorkflow() + ) super.init(nibName: nil, bundle: nil) } @@ -474,7 +471,7 @@ This works, but with no animation between the two screens it's pretty unsatisfyi ### Back Stack and "Containers" -We want to put our different screens in a navigation controller. Because we want all of our navigation state to be declarative, we need to use the `BackStackContainer` to do this - by registering and using the `BackStackScreen`: +We want to put our different screens in a navigation controller. Because we want all of our navigation state to be declarative, we need to use the `BackStackContainer` to do this - by using the `BackStackScreen`: ```swift public struct BackStackScreen: Screen { @@ -488,8 +485,6 @@ public struct BackStackScreen: Screen { The `BackStackScreen` contains a list of all screens in the back stack that are specified on each render pass. -Register the `BackStackScreen` so that we can use it in the TutorialContainerViewController: - ```swift import UIKit import Workflow @@ -501,19 +496,10 @@ public final class TutorialContainerViewController: UIViewController { let containerViewController: UIViewController public init() { - // Create a view registry. This will allow the infrastructure to map `Screen` types to their respective view controller type. - var viewRegistry = ViewRegistry() - // Register the `WelcomeScreen` and view controller with the convenience method the template provided. - viewRegistry.registerWelcomeScreen() - // Register the `TodoListScreen` and view controller with the convenience method the template provided. - viewRegistry.registerTodoListScreen() - // Register the `BackStackContainer`, which provides a container for the `BackStackScreen`. - viewRegistry.registerBackStackContainer() - - // Create a `ContainerViewController` with the `RootWorkflow` as the root workflow, with the view registry we just created. + // Create a `ContainerViewController` with the `RootWorkflow` as the root workflow. containerViewController = ContainerViewController( - workflow: RootWorkflow(), - viewRegistry: viewRegistry) + workflow: RootWorkflow() + ) super.init(nibName: nil, bundle: nil) } diff --git a/swift/Samples/Tutorial/Tutorial3.md b/swift/Samples/Tutorial/Tutorial3.md index 705814232..8d0207113 100644 --- a/swift/Samples/Tutorial/Tutorial3.md +++ b/swift/Samples/Tutorial/Tutorial3.md @@ -48,10 +48,10 @@ final class TodoEditViewController: ScreenViewController { // The `todoEditView` has all the logic for displaying the todo and editing. let todoEditView: TodoEditView - required init(screen: TodoEditScreen, viewRegistry: ViewRegistry) { + required init(screen: TodoEditScreen) { self.todoEditView = TodoEditView(frame: .zero) - super.init(screen: screen, viewRegistry: viewRegistry) + super.init(screen: screen) update(with: screen) } @@ -85,6 +85,16 @@ struct TodoEditScreen: Screen { } ``` +The `Screen` protocol also requires a `viewControllerDescription` property. This describes the `UIViewController` that will be used to render the screen: + +```swift +extension TodoEditScreen { + var viewControllerDescription: ViewControllerDescription { + TodoEditViewController.description(for: self) + } +} +``` + Then update the view with the data from the screen: ```swift @@ -106,35 +116,6 @@ final class TodoEditViewController: ScreenViewController { } ``` -Finally, register the `TodoEditScreen` in the `TutorialContainerViewController` with the viewRegistry: - -```swift -public final class TutorialContainerViewController: UIViewController { - let containerViewController: UIViewController - - public init() { - // Create a view registry. This will allow the infrastructure to map `Screen` types to their respective view controller type. - var viewRegistry = ViewRegistry() - // Register the `WelcomeScreen` and view controller with the convenience method the template provided. - viewRegistry.registerWelcomeScreen() - // Register the `TodoListScreen` and view controller with the convenience method the template provided. - viewRegistry.registerTodoListScreen() - // Register the `BackStackContainer`, which provides a container for the `BackStackScreen`. - viewRegistry.registerBackStackContainer() - // Register the `TodoEditScreen` and view controller with the convenience method the template provided. - viewRegistry.registerTodoEditScreen() - - // Create a `ContainerViewController` with the `RootWorkflow` as the root workflow, with the view registry we just created. - containerViewController = ContainerViewController( - workflow: RootWorkflow(), - viewRegistry: viewRegistry) - - super.init(nibName: nil, bundle: nil) - } - - // ... rest of the implementation ... -``` - #### TodoEditWorkflow Now that we have our screen and view controller, update the `TodoEditWorkflow` to emit this screen as the rendering. diff --git a/swift/Tooling/Templates/Screen (View Controller).xctemplate/___FILEBASENAME___Screen.swift b/swift/Tooling/Templates/Screen (View Controller).xctemplate/___FILEBASENAME___Screen.swift index 03f0826e0..51be7c9b0 100755 --- a/swift/Tooling/Templates/Screen (View Controller).xctemplate/___FILEBASENAME___Screen.swift +++ b/swift/Tooling/Templates/Screen (View Controller).xctemplate/___FILEBASENAME___Screen.swift @@ -9,13 +9,17 @@ struct ___VARIABLE_productName___Screen: Screen { // It should also contain callbacks for any UI events, for example: // var onButtonTapped: () -> Void + + var viewControllerDescription: ViewControllerDescription { + return ___VARIABLE_productName___ViewController.description(for: self) + } } final class ___VARIABLE_productName___ViewController: ScreenViewController<___VARIABLE_productName___Screen> { - required init(screen: ___VARIABLE_productName___Screen, viewRegistry: ViewRegistry) { - super.init(screen: screen, viewRegistry: viewRegistry) + required init(screen: ___VARIABLE_productName___Screen) { + super.init(screen: screen) update(with: screen) } @@ -28,12 +32,3 @@ final class ___VARIABLE_productName___ViewController: ScreenViewController<___VA } } - - -extension ViewRegistry { - - public mutating func register___VARIABLE_productName___Screen() { - self.register(screenViewControllerType: ___VARIABLE_productName___ViewController.self) - } - -}