Skip to content

Instantly share code, notes, and snippets.

@ollieatkinson
Last active May 15, 2024 06:08
Show Gist options
  • Save ollieatkinson/08b5bea50c4eb7efac30d071aedc3183 to your computer and use it in GitHub Desktop.
Save ollieatkinson/08b5bea50c4eb7efac30d071aedc3183 to your computer and use it in GitHub Desktop.
OnChangeObservable property wrapper
@propertyWrapper
public struct OnChangeObservable<Value: Equatable> {
private var storage: Storage
public var wrappedValue: Value {
get { storage.value }
set { storage.value = newValue }
}
public var projectedValue: OnChangeObservable<Value> {
get { self }
set { self = newValue }
}
public init(wrappedValue: Value) {
self.storage = Storage(value: wrappedValue)
}
public mutating func addListener(_ listener: @escaping (Value) -> Void) -> Listener {
copyOnWrite()
return storage.addListener(listener)
}
private mutating func copyOnWrite() {
if !isKnownUniquelyReferenced(&storage) {
storage = Storage(copying: storage)
}
}
public class Listener {
deinit { cancel() }
public let id: UUID
let cancel: () -> Void
init(id: UUID, cancel: @escaping () -> Void) {
self.id = id
self.cancel = cancel
}
}
private class Storage {
var value: Value {
didSet { notifyListeners(oldValue: oldValue) }
}
init(value: Value) {
self.value = value
}
init(copying storage: Storage) {
self.value = storage.value
self.listeners = storage.listeners
}
private var listeners: [UUID: (Value) -> Void] = [:]
func addListener(_ listener: @escaping (Value) -> Void) -> Listener {
let id = UUID()
listeners[id] = listener
return Listener(id: id) { [weak self] in
self?.listeners.removeValue(forKey: id)
}
}
func notifyListeners(oldValue: Value) {
guard oldValue != value else { return }
for (_, listener) in listeners {
listener(value)
}
}
}
}
@ollieatkinson
Copy link
Author

struct A {
    @OnChangeObservable var i: Int?
}

var a: A = A()
let token = a.$i.addListener { i in
    print("💥", i ?? "nil")
}

a.i = 1 // 💥 1
a.i = 2 // 💥 2

token.cancel()

a.i = 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment