- You need to development snapshot from swift.org
- After it is installed, you need to activate it on your project via the menu Xcode > Toolchains
- You need to add an
OTHER_SWIFT_FLAGS
setting on the target/project you can paste this into the Xcode UI:-Xfrontend -load-plugin-library -Xfrontend ${TOOLCHAIN_DIR}/usr/lib/swift/host/plugins/libObservationMacros.dylib
Created
May 12, 2023 18:37
-
-
Save allenhumphreys/5b5d8d601391e25234e7b6bc39a45bec to your computer and use it in GitHub Desktop.
Swift 5.9 Observability
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
@available(iOS 9999, *) | |
@main | |
struct ObservabilityApp: App { | |
var body: some Scene { | |
WindowGroup { | |
ContentView() | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@available(iOS 9999, *) | |
struct ContentView: View { | |
let model: Model = Model() | |
@StateObject var renderer = SwiftUIPretendRenderer() | |
var body: some View { | |
renderer.observe { | |
mainContent | |
} | |
} | |
var mainContent: some View { | |
Form { | |
Section("A basic property") { | |
Text("Updating this property always triggers the new value to be visible") | |
Button("Update") { | |
// Normal property, does the normal things | |
model.property = "\(Int.random(in: 1...1000))" | |
} | |
Text(model.property) | |
} | |
Section("An ignored property") { | |
Text("This property is ignored, so changing the value doesn't reflect in the UI until something else triggers a render") | |
Button("Update") { | |
// This property is ignored, so changes to it don't trigger a render | |
model.ignoredProperty = "\(Int.random(in: 1...1000))" | |
} | |
Text(model.ignoredProperty) | |
} | |
Section("A property that is not read") { | |
Text(try! AttributedString(markdown: "Because this property is not read within `withObservationTracking`, changing it will never trigger a render. This is similar to if the property is ignored.")) | |
Button("Update") { | |
// This property is not read, therefore changing it doesn't trigger a render | |
model.nonAccessedProperty = "\(Int.random(in: 1...1000))" | |
} | |
} | |
Section("A text binding") { | |
Text(try! AttributedString(markdown: "A normal model-backed `String` for a `TextField` `Binding`")) | |
TextField( | |
"Enter Some Words", | |
// I wonder if we'll see a convenience for creating Binding which is currently | |
// the responsibility of StateObject and ObservedObject | |
text: Binding( | |
get: { | |
model.textFieldValue | |
}, | |
set: { | |
model.textFieldValue = $0 | |
} | |
) | |
) | |
.textFieldStyle(.roundedBorder) | |
Text("You entered: \(model.textFieldValue.isEmpty ? "Nothing yet" : model.textFieldValue)") | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@available(iOS 9999, *) | |
@Observable public final class Model { | |
var property: String = "Start" | |
@ObservationIgnored var ignoredProperty: String = "Start" | |
var nonAccessedProperty: String = "Start" | |
var textFieldValue: String = "" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@available(iOS 9999, *) | |
final class SwiftUIPretendRenderer: ObservableObject { | |
@MainActor | |
@Published | |
var reRenderPlease: Void = () | |
@Sendable | |
func scheduleRender() { | |
Task { @MainActor in | |
reRenderPlease = () | |
} | |
} | |
func observe<T>(_ work: () -> T) -> T { | |
// I think SwiftUI will effectively use this function to implicitly watch for changes | |
return withObservationTracking( | |
work, | |
onChange: scheduleRender | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment