18
18
19
19
import ReactiveSwift
20
20
import UIKit
21
+ import ViewEnvironmentUI
21
22
import Workflow
22
23
23
- /// Drives view controllers from a root Workflow.
24
24
public final class WorkflowHostingController < ScreenType, Output> : UIViewController where ScreenType: Screen {
25
+ public typealias CustomizeEnvironment = ( inout ViewEnvironment ) -> Void
26
+
25
27
/// Emits output events from the bound workflow.
26
28
public var output : Signal < Output , Never > {
27
- return workflowHost. output
29
+ workflowHost. output
28
30
}
29
31
30
32
private( set) var rootViewController : UIViewController
@@ -33,28 +35,34 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
33
35
34
36
private let ( lifetime, token) = Lifetime . make ( )
35
37
36
- public var rootViewEnvironment : ViewEnvironment {
38
+ public var customizeEnvironment : CustomizeEnvironment {
37
39
didSet {
38
- update ( screen : workflowHost . rendering . value , environment : rootViewEnvironment )
40
+ setNeedsEnvironmentUpdate ( )
39
41
}
40
42
}
41
43
42
44
public init < W: AnyWorkflowConvertible > (
43
45
workflow: W ,
44
- rootViewEnvironment : ViewEnvironment = . empty ,
45
- observers : [ WorkflowObserver ] = [ ]
46
+ observers : [ WorkflowObserver ] = [ ] ,
47
+ customizeEnvironment : @escaping CustomizeEnvironment = { _ in }
46
48
) where W. Rendering == ScreenType , W. Output == Output {
47
49
self . workflowHost = WorkflowHost (
48
50
workflow: workflow. asAnyWorkflow ( ) ,
49
51
observers: observers
50
52
)
51
53
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
53
62
. rendering
54
63
. value
55
- . buildViewController ( in: rootViewEnvironment)
56
-
57
- self . rootViewEnvironment = rootViewEnvironment
64
+ . viewControllerDescription ( environment: customizedEnvironment)
65
+ . buildViewController ( )
58
66
59
67
super. init ( nibName: nil , bundle: nil )
60
68
@@ -66,9 +74,7 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
66
74
. signal
67
75
. take ( during: lifetime)
68
76
. 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)
72
78
}
73
79
}
74
80
@@ -81,15 +87,33 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
81
87
fatalError ( " init(coder:) has not been implemented " )
82
88
}
83
89
90
+ private func update( screen: ScreenType ) {
91
+ update ( screen: screen, environment: environment)
92
+ }
93
+
84
94
private func update( screen: ScreenType , environment: ViewEnvironment ) {
95
+ let previousRoot = rootViewController
96
+
85
97
update ( child: \. rootViewController, with: screen, in: environment)
86
98
99
+ if previousRoot !== rootViewController {
100
+ setNeedsEnvironmentUpdate ( )
101
+ }
102
+
87
103
updatePreferredContentSizeIfNeeded ( )
88
104
}
89
105
90
106
override public func viewDidLoad( ) {
91
107
super. viewDidLoad ( )
92
108
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
+
93
117
view. backgroundColor = . white
94
118
95
119
rootViewController. view. frame = view. bounds
@@ -98,37 +122,43 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
98
122
updatePreferredContentSizeIfNeeded ( )
99
123
}
100
124
125
+ override public func viewWillLayoutSubviews( ) {
126
+ super. viewWillLayoutSubviews ( )
127
+
128
+ applyEnvironmentIfNeeded ( )
129
+ }
130
+
101
131
override public func viewDidLayoutSubviews( ) {
102
132
super. viewDidLayoutSubviews ( )
103
133
rootViewController. view. frame = view. bounds
104
134
}
105
135
106
136
override public var childForStatusBarStyle : UIViewController ? {
107
- return rootViewController
137
+ rootViewController
108
138
}
109
139
110
140
override public var childForStatusBarHidden : UIViewController ? {
111
- return rootViewController
141
+ rootViewController
112
142
}
113
143
114
144
override public var childForHomeIndicatorAutoHidden : UIViewController ? {
115
- return rootViewController
145
+ rootViewController
116
146
}
117
147
118
148
override public var childForScreenEdgesDeferringSystemGestures : UIViewController ? {
119
- return rootViewController
149
+ rootViewController
120
150
}
121
151
122
152
override public var supportedInterfaceOrientations : UIInterfaceOrientationMask {
123
- return rootViewController. supportedInterfaceOrientations
153
+ rootViewController. supportedInterfaceOrientations
124
154
}
125
155
126
156
override public var preferredStatusBarUpdateAnimation : UIStatusBarAnimation {
127
- return rootViewController. preferredStatusBarUpdateAnimation
157
+ rootViewController. preferredStatusBarUpdateAnimation
128
158
}
129
159
130
160
override public var childViewControllerForPointerLock : UIViewController ? {
131
- return rootViewController
161
+ rootViewController
132
162
}
133
163
134
164
override public func preferredContentSizeDidChange(
@@ -150,4 +180,14 @@ public final class WorkflowHostingController<ScreenType, Output>: UIViewControll
150
180
}
151
181
}
152
182
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
+
153
193
#endif
0 commit comments