public import Foundation extension KeyPath: @unchecked @retroactive Sendable where Value: Sendable {} public extension NSObjectProtocol where Self: NSObject { /// Returns an `AsyncSequence` of `Value`s for all changes to the given key path on this object. /// /// - Parameters: /// - keyPath: The key path to observe on this object. The value must be `Sendable`. /// - options: KVO options to use for observation. Defaults to an empty set. func values( for keyPath: KeyPath, options: NSKeyValueObservingOptions = [] ) -> AsyncStream { let (stream, continuation) = AsyncStream.makeStream() let token: NSKeyValueObservation? = self.observe(keyPath, options: options) { object, _ in continuation.yield(object[keyPath: keyPath]) } // A nice side-effect of this is that the stream retains the token automatically. let locker = ValueLocker(value: token) continuation.onTermination = { _ in locker.modify { $0 = nil } } return stream } } @available(iOS 18, macOS 15, *) public extension NSObjectProtocol where Self: NSObject { /// Observes changes to the specified key path on the object and asynchronously yields each value. Values must be `Sendable`. /// /// - Parameters: /// - keyPath: The key path to observe on this object. The value must be `Sendable`. /// - options: KVO options to use for observation. Defaults to an empty set. /// - changeHandler: A closure that's executed with each new value. func monitorValues( for keyPath: KeyPath, options: NSKeyValueObservingOptions = [], changeHandler: @escaping (Value) -> Void ) -> any AsyncCancellable { values(for: keyPath, options: options) .monitor(changeHandler) } } @available(iOS, introduced: 17, obsoleted: 18) @available(macOS, introduced: 14, obsoleted: 15) public extension NSObjectProtocol where Self: NSObject { /// Observes changes to the specified key path on the object and asynchronously yields each value. Values must be `Sendable`. /// /// - Parameters: /// - keyPath: The key path to observe on this object. The value must be `Sendable`. /// - options: KVO options to use for observation. Defaults to an empty set. /// - changeHandler: A closure that's executed with each new value. func monitorValues( for keyPath: KeyPath, options: NSKeyValueObservingOptions = [], changeHandler: @escaping @Sendable (Value) -> Void ) -> any AsyncCancellable { values(for: keyPath, options: options) .monitor(changeHandler) } }