Created
September 16, 2023 13:16
-
-
Save helje5/0a4eb12fcb7ca22b17e0d8c469aa3f26 to your computer and use it in GitHub Desktop.
An `Observable` property wrapper that only creates the observable value once
This file contains hidden or 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
/** | |
* An Observable property wrapper that only creates the observable | |
* value once. | |
* | |
* Example: | |
* ```swift | |
* @Observable class Item { | |
* } | |
* | |
* struct MyView: View { | |
* @StateObservable var item = Item() | |
* } | |
* ``` | |
*/ | |
@propertyWrapper | |
struct StateObservable<ObjectType: Observable>: DynamicProperty { | |
final class ObservationBridge: ObservableObject { | |
let observable : ObjectType | |
init(_ observable: ObjectType) { self.observable = observable } | |
} | |
@StateObject private var bridge : ObservationBridge | |
init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType) { | |
_bridge = .init(wrappedValue: ObservationBridge(thunk())) | |
} | |
@MainActor var wrappedValue: ObjectType { bridge.observable } | |
} |
Related discussion about iOS 17 Observable
objects and @State
:
https://mastodon.social/@helge/111074594371761479
Here is another version that overloads @StateObject
, this doesn't work w/ function calls though (would have to qualify), and is also slower in property access:
extension StateObject {
/**
* Use Observable's with `@StateObject`.
*
* Example:
* ```swift
* @Observable class Item {
* }
*
* struct MyView: View {
* @StateObject var item = Item()
* }
* ```
*/
init<T>(wrappedValue thunk: @autoclosure @escaping () -> T)
where T: Observable, ObjectType == ObservationBridge<T>
{
self.init(wrappedValue: ObservationBridge(thunk()))
}
}
@dynamicMemberLookup
final class ObservationBridge<ObjectType>: ObservableObject {
let observable : ObjectType
init(_ observable: ObjectType) { self.observable = observable }
public subscript<V>(dynamicMember keyPath: KeyPath<ObjectType, V>) -> V {
observable[keyPath: keyPath]
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
P.S.: Don't use it, but keep your state outside of views! 😜