Skip to content

Instantly share code, notes, and snippets.

@vlas-voloshin
Created September 13, 2025 05:46
Show Gist options
  • Select an option

  • Save vlas-voloshin/2096bf5528db39b90e6fc15206eee3bd to your computer and use it in GitHub Desktop.

Select an option

Save vlas-voloshin/2096bf5528db39b90e6fc15206eee3bd to your computer and use it in GitHub Desktop.
Replacement for StateObject property wrapper for Observable objects
import SwiftUI
/// A property wrapper type that instantiates an observable object.
///
/// This is a wrapper for `State` that works with `Observable` values and provides "lazy" initialization semantics similar to `StateObject`.
@MainActor @propertyWrapper
struct LazyState<Value: Observable & AnyObject>: @preconcurrency DynamicProperty {
var wrappedValue: Value {
guard let value = storage.value else {
fatalError("Attempted to access LazyState's value before it's constructed. Are you calling it outside of a View's body?")
}
return value
}
/// Creates a new state object with an initial wrapped value.
///
/// See documentation of `StateObject.init(wrappedValue:)` for a description of this initializer's semantics and the caveats around initialization using external data.
init(wrappedValue thunk: @autoclosure @escaping () -> Value) {
// `State` initializer will reject the passed value if it was already initialized within a view with the same identity, leaving the existing storage intact
_storage = State(initialValue: Storage(thunk))
}
// MARK: - DynamicProperty
func update() {
storage.initializeIfNeeded()
}
// MARK: - Private
@MainActor
private final class Storage {
private(set) var value: Value?
private var thunk: (() -> Value)?
init(_ thunk: @escaping () -> Value) { self.thunk = thunk }
func initializeIfNeeded() {
// Clear out initialization thunk once the value is constructed to release any intermediary resources
if value == nil, let thunk = thunk.take() {
value = thunk()
}
}
}
@State private var storage: Storage
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment