Skip to content

Commit 9dab8e5

Browse files
committed
Add bridging to WorkflowHostingController
1 parent 89f8104 commit 9dab8e5

File tree

1 file changed

+60
-20
lines changed

1 file changed

+60
-20
lines changed

WorkflowUI/Sources/Hosting/WorkflowHostingController.swift

+60-20
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818

1919
import ReactiveSwift
2020
import UIKit
21+
import ViewEnvironmentUI
2122
import Workflow
2223

23-
/// Drives view controllers from a root Workflow.
2424
public 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

Comments
 (0)