1818
1919import ReactiveSwift
2020import UIKit
21+ import ViewEnvironmentUI
2122import Workflow
2223
23- /// Drives view controllers from a root Workflow.
2424public final class WorkflowHostingController < ScreenType, Output> : UIViewController where ScreenType: Screen {
25+ public typealias CustomizeEnvironment = ( inout ViewEnvironment ) -> Void
26+
2527 /// Emits output events from the bound workflow.
2628 public var output : Signal < Output , Never > {
27- return workflowHost. output
29+ workflowHost. output
2830 }
2931
3032 private( set) var rootViewController : UIViewController
@@ -33,28 +35,34 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
3335
3436 private let ( lifetime, token) = Lifetime . make ( )
3537
36- public var rootViewEnvironment : ViewEnvironment {
38+ public var customizeEnvironment : CustomizeEnvironment {
3739 didSet {
38- update ( screen : workflowHost . rendering . value , environment : rootViewEnvironment )
40+ setNeedsEnvironmentUpdate ( )
3941 }
4042 }
4143
4244 public init < W: AnyWorkflowConvertible > (
4345 workflow: W ,
44- rootViewEnvironment : ViewEnvironment = . empty ,
45- observers : [ WorkflowObserver ] = [ ]
46+ observers : [ WorkflowObserver ] = [ ] ,
47+ customizeEnvironment : @escaping CustomizeEnvironment = { _ in }
4648 ) where W. Rendering == ScreenType , W. Output == Output {
4749 self . workflowHost = WorkflowHost (
4850 workflow: workflow. asAnyWorkflow ( ) ,
4951 observers: observers
5052 )
5153
52- self . rootViewController = workflowHost
54+ self . customizeEnvironment = customizeEnvironment
55+
56+ // Customize the default environment for the first render so that we can perform updates and query view
57+ // controller containment methods before the view has been added to the hierarchy.
58+ var customizedEnvironment : ViewEnvironment = . empty
59+ customizeEnvironment ( & customizedEnvironment)
60+
61+ rootViewController = workflowHost
5362 . rendering
5463 . value
55- . buildViewController ( in: rootViewEnvironment)
56-
57- self . rootViewEnvironment = rootViewEnvironment
64+ . viewControllerDescription ( environment: customizedEnvironment)
65+ . buildViewController ( )
5866
5967 super. init ( nibName: nil , bundle: nil )
6068
@@ -66,9 +74,7 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
6674 . signal
6775 . take ( during: lifetime)
6876 . observeValues { [ weak self] screen in
69- guard let self = self else { return }
70-
71- self . update ( screen: screen, environment: self . rootViewEnvironment)
77+ self ? . update ( screen: screen)
7278 }
7379 }
7480
@@ -81,15 +87,33 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
8187 fatalError ( " init(coder:) has not been implemented " )
8288 }
8389
90+ private func update( screen: ScreenType ) {
91+ update ( screen: screen, environment: environment)
92+ }
93+
8494 private func update( screen: ScreenType , environment: ViewEnvironment ) {
95+ let previousRoot = rootViewController
96+
8597 update ( child: \. rootViewController, with: screen, in: environment)
8698
99+ if previousRoot !== rootViewController {
100+ setNeedsEnvironmentUpdate ( )
101+ }
102+
87103 updatePreferredContentSizeIfNeeded ( )
88104 }
89105
90106 override public func viewDidLoad( ) {
91107 super. viewDidLoad ( )
92108
109+ let environment = self . environment
110+
111+ // Update before loading the contained view controller's view so that the environment can fully propagate
112+ // before descendant views have loaded.
113+ // Many screens rely on `ViewEnvironment` validations in viewDidLoad which could be using the initial
114+ // `ViewEnvironment` without this explicit update.
115+ update ( screen: workflowHost. rendering. value, environment: environment)
116+
93117 view. backgroundColor = . white
94118
95119 rootViewController. view. frame = view. bounds
@@ -98,37 +122,43 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
98122 updatePreferredContentSizeIfNeeded ( )
99123 }
100124
125+ override public func viewWillLayoutSubviews( ) {
126+ super. viewWillLayoutSubviews ( )
127+
128+ applyEnvironmentIfNeeded ( )
129+ }
130+
101131 override public func viewDidLayoutSubviews( ) {
102132 super. viewDidLayoutSubviews ( )
103133 rootViewController. view. frame = view. bounds
104134 }
105135
106136 override public var childForStatusBarStyle : UIViewController ? {
107- return rootViewController
137+ rootViewController
108138 }
109139
110140 override public var childForStatusBarHidden : UIViewController ? {
111- return rootViewController
141+ rootViewController
112142 }
113143
114144 override public var childForHomeIndicatorAutoHidden : UIViewController ? {
115- return rootViewController
145+ rootViewController
116146 }
117147
118148 override public var childForScreenEdgesDeferringSystemGestures : UIViewController ? {
119- return rootViewController
149+ rootViewController
120150 }
121151
122152 override public var supportedInterfaceOrientations : UIInterfaceOrientationMask {
123- return rootViewController. supportedInterfaceOrientations
153+ rootViewController. supportedInterfaceOrientations
124154 }
125155
126156 override public var preferredStatusBarUpdateAnimation : UIStatusBarAnimation {
127- return rootViewController. preferredStatusBarUpdateAnimation
157+ rootViewController. preferredStatusBarUpdateAnimation
128158 }
129159
130160 override public var childViewControllerForPointerLock : UIViewController ? {
131- return rootViewController
161+ rootViewController
132162 }
133163
134164 override public func preferredContentSizeDidChange(
@@ -150,4 +180,14 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
150180 }
151181}
152182
183+ extension WorkflowHostingController : ViewEnvironmentCustomizing , ViewEnvironmentObserving {
184+ public func apply( environment: ViewEnvironment ) {
185+ update ( screen: workflowHost. rendering. value, environment: environment)
186+ }
187+
188+ public func customize( environment: inout ViewEnvironment ) {
189+ customizeEnvironment ( & environment)
190+ }
191+ }
192+
153193#endif
0 commit comments