Skip to content

Commit f5d0b68

Browse files
authored
Add setRootView implementation (#84)
* Add setRootView implementation * Fix Linux build issue * Fix WASI Thread issue
1 parent ec4ccd0 commit f5d0b68

File tree

7 files changed

+154
-30
lines changed

7 files changed

+154
-30
lines changed

Sources/OpenSwiftUI/Core/Update/Update.swift

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
internal import COpenSwiftUI
99
internal import OpenGraphShims
10+
import Foundation
1011

1112
extension MovableLock {
1213
@inline(__always)
@@ -43,6 +44,13 @@ enum Update {
4344
lock.unlock()
4445
}
4546

47+
@inline(__always)
48+
static func perform<Value>(_ body: () -> Value) -> Value {
49+
begin()
50+
defer { end() }
51+
return body()
52+
}
53+
4654
static func enqueueAction(_ action: @escaping () -> Void) {
4755
begin()
4856
actions.append(action)
@@ -73,11 +81,33 @@ enum Update {
7381

7482
@inline(__always)
7583
static func syncMain(_ body: () -> Void) {
76-
// TODO
77-
fatalError("TODO")
84+
#if os(WASI)
85+
// FIXME: See #76
86+
body()
87+
#else
88+
if Thread.isMainThread {
89+
body()
90+
} else {
91+
withoutActuallyEscaping(body) { escapableBody in
92+
MovableLock.syncMain(lock: lock) {
93+
#if canImport(Darwin)
94+
AnyRuleContext(attribute: AnyOptionalAttribute.current.identifier).update(body: escapableBody)
95+
#else
96+
fatalError("See #39")
97+
#endif
98+
}
99+
}
100+
}
101+
#endif
78102
}
79103
}
80104

81105
extension Update {
82106
private class TraceHost {}
83107
}
108+
109+
// FIXME: migrate to use @_extern(c, "xx") in Swift 6
110+
extension MovableLock {
111+
@_silgen_name("_MovableLockSyncMain")
112+
static func syncMain(lock: MovableLock ,body: @escaping () -> Void)
113+
}

Sources/OpenSwiftUI/Core/View/ViewGraph.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,13 @@ final class ViewGraph: GraphHost {
180180
// TODO
181181
}
182182

183+
func setRootView<V: View>(_ view: V) {
184+
#if canImport(Darwin)
185+
@Attribute(identifier: rootView)
186+
var rootView: V
187+
rootView = view
188+
#endif
189+
}
183190

184191
// MARK: - Override Methods
185192

Sources/OpenSwiftUI/Core/View/ViewGraphDelegate.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,13 @@ protocol ViewGraphDelegate: GraphDelegate {
1212
func focusDidChange()
1313
func rootTransform() -> ViewTransform
1414
}
15+
16+
extension ViewGraphDelegate {
17+
func updateGraph<V>(body: (GraphHost) -> V) -> V {
18+
updateViewGraph(body: body)
19+
}
20+
21+
func rootTransform() -> ViewTransform {
22+
ViewTransform()
23+
}
24+
}

Sources/OpenSwiftUI/Core/View/ViewRendererHost.swift

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,31 @@ protocol ViewRendererHost: ViewGraphDelegate {
1111
var viewGraph: ViewGraph { get }
1212
var currentTimestamp: Time { get set }
1313
var propertiesNeedingUpdate: ViewRendererHostProperties { get set }
14+
func addImplicitPropertiesNeedingUpdate(to properties: inout ViewRendererHostProperties)
1415
var isRendering: Bool { get set }
16+
func updateRootView()
1517
func requestUpdate(after: Double)
1618
}
1719

20+
// MARK: - ViewRendererHost's default implementation for ViewGraphDelegate
21+
22+
extension ViewRendererHost {
23+
func updateViewGraph<Value>(body: (ViewGraph) -> Value) -> Value {
24+
Update.perform {
25+
OGGraph.withoutUpdate {
26+
updateGraph()
27+
return body(viewGraph)
28+
}
29+
}
30+
}
31+
}
32+
1833
extension ViewRendererHost {
34+
func initializeViewGraph() {
35+
viewGraph.delegate = self
36+
// TODO: Signpost related
37+
}
38+
1939
func invalidateProperties(_ properties: ViewRendererHostProperties, mayDeferUpdate: Bool) {
2040
Update.lock.withLock {
2141
guard !propertiesNeedingUpdate.contains(properties) else {
@@ -36,28 +56,50 @@ extension ViewRendererHost {
3656
}
3757

3858
func render(interval: Double, updateDisplayList: Bool = true) {
39-
Update.begin()
40-
defer { Update.end() }
41-
guard !isRendering else {
42-
return
43-
}
44-
let update = { [self] in
45-
currentTimestamp.advancing(by: interval)
46-
let time = currentTimestamp
47-
viewGraph.flushTransactions()
48-
// Signpost.renderUpdate
49-
// TODO
50-
viewGraph.updateOutputs(at: time)
59+
Update.perform {
60+
guard !isRendering else {
61+
return
62+
}
63+
let update = { [self] in
64+
currentTimestamp.advancing(by: interval)
65+
let time = currentTimestamp
66+
viewGraph.flushTransactions()
67+
// Signpost.renderUpdate
68+
// TODO
69+
viewGraph.updateOutputs(at: time)
70+
}
71+
if Signpost.render.isEnabled {
72+
// TODO: Signpost related
73+
update()
74+
} else {
75+
update()
76+
}
5177
}
52-
if Signpost.render.isEnabled {
53-
// TODO: Signpost related
54-
update()
55-
} else {
56-
update()
78+
}
79+
80+
static func makeRootView<V: View>(_ view: V) -> some View {
81+
view/*.modifier(HitTestBindingModifier())*/
82+
}
83+
84+
func updateGraph() {
85+
var properties = propertiesNeedingUpdate
86+
addImplicitPropertiesNeedingUpdate(to: &properties)
87+
guard !properties.isEmpty else { return }
88+
Update.syncMain {
89+
func update(_ property: ViewRendererHostProperties, body: () -> Void) {
90+
if properties.contains(property) {
91+
propertiesNeedingUpdate.remove(property)
92+
}
93+
body()
94+
}
95+
update(.rootView) { updateRootView() }
96+
// TODO:
5797
}
5898
}
5999
}
60100

61101
struct ViewRendererHostProperties: OptionSet {
62102
let rawValue: UInt16
103+
104+
static var rootView: ViewRendererHostProperties { ViewRendererHostProperties(rawValue: 1 << 0) }
63105
}

Sources/OpenSwiftUI/Integration/UIKit/UIHostingController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ open class UIHostingController<Content> : UIViewController where Content : View
88
var host: _UIHostingView<Content>
99

1010
override open dynamic var keyCommands: [UIKeyCommand]? {
11-
fatalError("Unimplemented")
11+
// TODO
12+
nil
1213
}
1314

1415
public init(rootView: Content) {

Sources/OpenSwiftUI/Integration/UIKit/UIHostingView.swift

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ open class _UIHostingView<Content>: UIView where Content: View {
1616
private var _rootView: Content
1717
var viewGraph: ViewGraph
1818
var currentTimestamp: Time = .zero
19-
var propertiesNeedingUpdate: ViewRendererHostProperties = []
19+
var propertiesNeedingUpdate: ViewRendererHostProperties = [.rootView] // FIXME
2020
var isRendering: Bool = false
2121
var inheritedEnvironment: EnvironmentValues?
2222
var environmentOverride: EnvironmentValues?
@@ -34,13 +34,30 @@ open class _UIHostingView<Content>: UIView where Content: View {
3434
// TODO
3535
// FIXME
3636
super.init(frame: .zero)
37+
38+
initializeViewGraph()
39+
// TODO
3740
}
3841

3942
@available(*, unavailable)
4043
public required init?(coder _: NSCoder) {
4144
fatalError("init(coder:) has not been implemented")
4245
}
4346

47+
func setRootView(_ view: Content, transaction: Transaction) {
48+
_rootView = view
49+
let mutation = CustomGraphMutation { [weak self] in
50+
guard let self else { return }
51+
updateRootView()
52+
}
53+
viewGraph.asyncTransaction(
54+
transaction,
55+
mutation: mutation,
56+
style: ._1,
57+
mayDeferUpdate: true
58+
)
59+
}
60+
4461
var rootView: Content {
4562
get { _rootView }
4663
set {
@@ -49,7 +66,10 @@ open class _UIHostingView<Content>: UIView where Content: View {
4966
}
5067
}
5168

52-
69+
func makeRootView() -> some View {
70+
_UIHostingView.makeRootView(rootView/*.modifier(EditModeScopeModifier(editMode: .default))*/)
71+
}
72+
5373
@available(macOS, unavailable)
5474
@available(watchOS, unavailable)
5575
final public func _viewDebugData() -> [_ViewDebug.Data] {
@@ -114,6 +134,13 @@ open class _UIHostingView<Content>: UIView where Content: View {
114134
}
115135

116136
extension _UIHostingView: ViewRendererHost {
137+
func addImplicitPropertiesNeedingUpdate(to _: inout ViewRendererHostProperties) {}
138+
139+
func updateRootView() {
140+
let rootView = makeRootView()
141+
viewGraph.setRootView(rootView)
142+
}
143+
117144
func requestUpdate(after: Double) {
118145
// TODO
119146
}
@@ -122,10 +149,6 @@ extension _UIHostingView: ViewRendererHost {
122149
// TODO
123150
}
124151

125-
func updateViewGraph<Value>(body: (ViewGraph) -> Value) -> Value {
126-
fatalError("TODO")
127-
}
128-
129152
func outputsDidChange(outputs: ViewGraph.Outputs) {
130153
// TODO
131154
}
@@ -138,10 +161,6 @@ extension _UIHostingView: ViewRendererHost {
138161
fatalError("TODO")
139162
}
140163

141-
func updateGraph<V>(body: (GraphHost) -> V) -> V {
142-
fatalError("TODO")
143-
}
144-
145164
func graphDidChange() {
146165
// TODO
147166
}

TestsHost/OpenSwiftUIUITests/View/View/AnyViewTests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,20 @@ final class AnyViewTests: XCTestCase {
3030
window.makeKeyAndVisible()
3131
vc.view.layoutSubviews()
3232
}
33+
34+
// @Test("BodyAccessor crash for non empty View instance", .bug("#81", relationship: .verifiesFix))
35+
func testBasicAnyViewWithProperty() throws {
36+
struct ContentView: View {
37+
var name = ""
38+
var body: some View {
39+
AnyView(EmptyView())
40+
}
41+
}
42+
let vc = UIHostingController(rootView: ContentView())
43+
let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
44+
window.rootViewController = vc
45+
window.makeKeyAndVisible()
46+
vc.view.layoutSubviews()
47+
}
3348
}
3449
#endif

0 commit comments

Comments
 (0)