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,9 +87,19 @@ 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
@@ -98,37 +114,43 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
98114 updatePreferredContentSizeIfNeeded ( )
99115 }
100116
117+ override public func viewWillLayoutSubviews( ) {
118+ super. viewWillLayoutSubviews ( )
119+
120+ applyEnvironmentIfNeeded ( )
121+ }
122+
101123 override public func viewDidLayoutSubviews( ) {
102124 super. viewDidLayoutSubviews ( )
103125 rootViewController. view. frame = view. bounds
104126 }
105127
106128 override public var childForStatusBarStyle : UIViewController ? {
107- return rootViewController
129+ rootViewController
108130 }
109131
110132 override public var childForStatusBarHidden : UIViewController ? {
111- return rootViewController
133+ rootViewController
112134 }
113135
114136 override public var childForHomeIndicatorAutoHidden : UIViewController ? {
115- return rootViewController
137+ rootViewController
116138 }
117139
118140 override public var childForScreenEdgesDeferringSystemGestures : UIViewController ? {
119- return rootViewController
141+ rootViewController
120142 }
121143
122144 override public var supportedInterfaceOrientations : UIInterfaceOrientationMask {
123- return rootViewController. supportedInterfaceOrientations
145+ rootViewController. supportedInterfaceOrientations
124146 }
125147
126148 override public var preferredStatusBarUpdateAnimation : UIStatusBarAnimation {
127- return rootViewController. preferredStatusBarUpdateAnimation
149+ rootViewController. preferredStatusBarUpdateAnimation
128150 }
129151
130152 override public var childViewControllerForPointerLock : UIViewController ? {
131- return rootViewController
153+ rootViewController
132154 }
133155
134156 override public func preferredContentSizeDidChange(
@@ -150,4 +172,14 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
150172 }
151173}
152174
175+ extension WorkflowHostingController : ViewEnvironmentCustomizing , ViewEnvironmentObserving {
176+ public func apply( environment: ViewEnvironment ) {
177+ update ( screen: workflowHost. rendering. value, environment: environment)
178+ }
179+
180+ public func customize( environment: inout ViewEnvironment ) {
181+ customizeEnvironment ( & environment)
182+ }
183+ }
184+
153185#endif
0 commit comments