Skip to content

Instantly share code, notes, and snippets.

@mattadatta
Last active November 17, 2025 13:55
Show Gist options
  • Select an option

  • Save mattadatta/67e8b0e6363f85ab6ee43f536a3a64b1 to your computer and use it in GitHub Desktop.

Select an option

Save mattadatta/67e8b0e6363f85ab6ee43f536a3a64b1 to your computer and use it in GitHub Desktop.
`@StateObject`-like property wrapper for `@Observable` types that provides an autoclosure initializer.
import SwiftUI
@propertyWrapper
@MainActor
public struct ObservableState<Value: AnyObject & Observable>: @MainActor DynamicProperty {
private final class Storage: ObservableObject {
var value: Value?
init() {
}
}
public typealias CreateValue = () -> Value
@StateObject private var storage: Storage = Storage()
private var createValue: CreateValue
public init(wrappedValue value: @autoclosure @escaping CreateValue) {
self.createValue = value
}
public var wrappedValue: Value {
get { storage.value! }
nonmutating set {
storage.value = newValue
}
}
public var projectedValue: Bindable<Value> {
Bindable(wrappedValue: wrappedValue)
}
public mutating func update() {
guard storage.value == nil else { return }
storage.value = createValue()
}
}
extension ObservableState: Sendable where Value: Sendable {
}
extension ObservableState where Value: ExpressibleByNilLiteral {
@inlinable
public init() {
self = .init(wrappedValue: nil)
}
}
//
// Example usage:
//
import SwiftUI
struct RootView: View {
@ObservableState private var viewModel = RootViewModel()
init() {
}
var body: some View {
VStack {
if viewModel.isEnabled {
// ...
} else {
// ...
}
}
}
}
@Observable
@MainActor
final class RootViewModel {
var isEnabled: Bool
init() {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment