Skip to content

Commit b90679e

Browse files
authored
fix(observation): restore body updates for observable state (#1104)
1 parent 02e5baf commit b90679e

7 files changed

Lines changed: 107 additions & 23 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// ObservationUXTests.swift
3+
// OpenSwiftUIUXTests
4+
5+
#if OPENSWIFTUI
6+
import OpenObservation
7+
import OpenSwiftUI
8+
#else
9+
import Observation
10+
import SwiftUI
11+
#endif
12+
import Testing
13+
14+
@MainActor
15+
@Suite
16+
struct ObservationUXTests {
17+
@Test
18+
func stateModelMutationUpdatesBody() async throws {
19+
var displayedCounts: [Int] = []
20+
let content = CounterView { displayedCounts.append($0) }
21+
22+
try await withUXTestHost(of: content) { host in
23+
try await host.waitUntil(displayedCounts == [0])
24+
for count in 1...2 {
25+
try await host.tap()
26+
try await host.waitUntil(displayedCounts.last == count)
27+
#expect(displayedCounts == Array(0...count))
28+
}
29+
}
30+
}
31+
}
32+
33+
@Observable
34+
private final class CounterModel {
35+
var count = 0
36+
}
37+
38+
private struct CounterView: View {
39+
@State private var model = CounterModel()
40+
let onChange: (Int) -> Void
41+
42+
var body: some View {
43+
let count = model.count
44+
Text("\(count)")
45+
.onTapGesture {
46+
model.count += 1
47+
}
48+
.onChange(of: count, initial: true) { _, newValue in
49+
onChange(newValue)
50+
}
51+
}
52+
}

‎Example/Project.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ let targets: [Target] = [
365365
dependencies: [
366366
.target(name: "TestingHost"),
367367
.external(name: "Hammer"),
368+
.external(name: "OpenSwiftUITestsSupport"),
368369
] + privateFrameworkDependencies,
369370
settings: settings(base: uxTestsSettings, xcconfig: "../Configurations/OpenSwiftUIUXTests.xcconfig")
370371
),

‎Example/Shared/Data/ObservationExample.swift‎

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,55 @@ import Observation
1010
import SwiftUI
1111
#endif
1212

13+
struct ObservationExample: View {
14+
var body: some View {
15+
VStack {
16+
ObservationColorExample()
17+
Divider()
18+
ObservationCounterExample()
19+
}
20+
}
21+
}
22+
1323
@Observable
14-
private class Model {
24+
private class ColorModel {
1525
var showRed = false
1626
}
1727

18-
struct ObservationExample: View {
19-
@State private var model = Model()
20-
21-
private var showRed: Bool {
22-
get { model.showRed }
23-
nonmutating set { model.showRed = newValue }
24-
}
28+
struct ObservationColorExample: View {
29+
@State private var model = ColorModel()
2530

2631
var body: some View {
2732
VStack {
28-
Color(platformColor: showRed ? .red : .blue)
29-
.frame(width: showRed ? 200 : 400, height: showRed ? 200 : 400)
30-
}
31-
.animation(.spring, value: showRed)
32-
.onTapGesture {
33-
showRed.toggle()
33+
Color(model.showRed ? .red : .blue)
34+
Text("Toggle")
35+
.onTapGesture {
36+
model.showRed.toggle()
37+
}
3438
}
3539
}
3640
}
3741

3842
#Preview {
39-
ObservationExample()
43+
ObservationColorExample()
44+
}
45+
46+
@Observable
47+
private final class CounterModel {
48+
var count = 0
49+
}
50+
51+
struct ObservationCounterExample: View {
52+
@State private var model = CounterModel()
53+
54+
var body: some View {
55+
Text("Tap to increase: \(model.count)")
56+
.onTapGesture {
57+
model.count += 1
58+
}
59+
}
60+
}
61+
62+
#Preview {
63+
ObservationCounterExample()
4064
}

‎Example/Tuist/Package.resolved‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Example/Tuist/Package.swift‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,11 @@ var packageProductTypes: [String: ProjectDescription.Product] = [
9393
"OpenSwiftUITestsSupport": ProjectDescription.Product.staticFramework,
9494
"OpenSwiftUISymbolDualTestsSupport": ProjectDescription.Product.staticFramework,
9595
"OpenAttributeGraphShims": ProjectDescription.Product.staticFramework,
96-
"OpenCoreGraphicsShims": ProjectDescription.Product.staticFramework,
97-
"OpenObservation": ProjectDescription.Product.staticFramework,
98-
"OpenQuartzCoreShims": ProjectDescription.Product.staticFramework,
99-
"OpenRenderBoxShims": ProjectDescription.Product.staticFramework,
96+
"OpenCoreGraphicsShims": ProjectDescription.Product.framework,
97+
"OpenObservation": ProjectDescription.Product.framework,
98+
"OpenObservationCxx": ProjectDescription.Product.staticFramework,
99+
"OpenQuartzCoreShims": ProjectDescription.Product.framework,
100+
"OpenRenderBoxShims": ProjectDescription.Product.framework,
100101
"SymbolLocator": ProjectDescription.Product.staticFramework,
101102
]
102103

@@ -146,6 +147,12 @@ let packageSettings = PackageSettings(
146147
defaultSettings: .essential,
147148
defaultConfiguration: "OpenSwiftUIDebug"
148149
),
150+
"OpenSwiftUITestsSupport": .settings(
151+
base: ["ENABLE_TESTING_SEARCH_PATHS": "YES"],
152+
configurations: openSwiftUIPackageConfigurations,
153+
defaultSettings: .essential,
154+
defaultConfiguration: "OpenSwiftUIDebug"
155+
),
149156
"OpenSwiftUI": .settings(
150157
base: openSwiftUITargetSettings,
151158
configurations: openSwiftUIPackageConfigurations,

‎Package.resolved‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Sources/OpenSwiftUICore/Data/Observation/ObservationUtils.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ private func installObservationSlow<T>(
140140
accessList: ObservationTracking._AccessList,
141141
attribute: Attribute<T>
142142
) {
143-
guard let subgraph = attribute.identifier.subgraph2 else {
143+
guard let subgraph = attribute.identifier.subgraphOrNil else {
144144
return
145145
}
146146
let weakViewGraph = WeakUncheckedSendable(ViewGraph.current)

0 commit comments

Comments
 (0)