11import Dependencies
2- import Foundation
32@_spi ( Internals) import DependenciesAdditionsBasics
3+ import Foundation
4+
45extension DependencyValues {
56 /// A dependency that exposes an ``UserDefaults.Dependency`` value that you can use to read and
67 /// write to `UserDefaults`.
@@ -47,7 +48,8 @@ extension UserDefaults {
4748 /// - Parameter key: The key that references this user preference.
4849 /// - Returns: An `AsyncSequence` of `T?` values, including the initial value.
4950 @_spi ( Internals)
50- public func values< T> ( forKey key: String ) -> AsyncMapSequence < AsyncStream < ( any Sendable ) ? > , T ? > {
51+ public func values< T> ( forKey key: String ) -> AsyncMapSequence < AsyncStream < ( any Sendable ) ? > , T ? >
52+ {
5153 self . _values ( key, T . self) . map { $0 as? T }
5254 }
5355 }
@@ -73,49 +75,49 @@ extension UserDefaults.Dependency: DependencyKey {
7375 // We use KVO to also get out-of-process changes
7476 AsyncStream ( ( any Sendable ) ? . self) { continuation in
7577 #if canImport(ObjectiveC)
76- final class Observer : NSObject , Sendable {
77- let key : String
78- let type : Any . Type
79- let onChange : @Sendable ( ( any Sendable ) ? ) -> Void
80- init (
81- key: String ,
82- type: Any . Type ,
83- onChange: @escaping @Sendable ( ( any Sendable ) ? ) -> Void
84- ) {
85- self . key = key
86- self . type = type
87- self . onChange = onChange
88- super. init ( )
89- }
78+ final class Observer : NSObject , Sendable {
79+ let key : String
80+ let type : Any . Type
81+ let onChange : @Sendable ( ( any Sendable ) ? ) -> Void
82+ init (
83+ key: String ,
84+ type: Any . Type ,
85+ onChange: @escaping @Sendable ( ( any Sendable ) ? ) -> Void
86+ ) {
87+ self . key = key
88+ self . type = type
89+ self . onChange = onChange
90+ super. init ( )
91+ }
9092
91- override func observeValue(
92- forKeyPath keyPath: String ? ,
93- of object: Any ? ,
94- change: [ NSKeyValueChangeKey : Any ] ? ,
95- context: UnsafeMutableRawPointer ?
96- ) {
97- self . onChange (
98- ( object as! UserDefaults ) . getSendable ( forKey: self . key, as: self . type)
99- )
93+ override func observeValue(
94+ forKeyPath keyPath: String ? ,
95+ of object: Any ? ,
96+ change: [ NSKeyValueChangeKey : Any ] ? ,
97+ context: UnsafeMutableRawPointer ?
98+ ) {
99+ self . onChange (
100+ ( object as! UserDefaults ) . getSendable ( forKey: self . key, as: self . type)
101+ )
102+ }
100103 }
101- }
102104
103- let object = Observer ( key: key, type: type) {
104- continuation. yield ( $0)
105- }
105+ let object = Observer ( key: key, type: type) {
106+ continuation. yield ( $0)
107+ }
106108
107- userDefaults. value. addObserver (
108- object,
109- forKeyPath: key,
110- options: [ . initial, . new] ,
111- context: nil
112- )
113- continuation. onTermination = { _ in
114- userDefaults. value. removeObserver ( object, forKeyPath: key)
115- }
109+ userDefaults. value. addObserver (
110+ object,
111+ forKeyPath: key,
112+ options: [ . initial, . new] ,
113+ context: nil
114+ )
115+ continuation. onTermination = { _ in
116+ userDefaults. value. removeObserver ( object, forKeyPath: key)
117+ }
116118 #else
117- print ( " AsyncStream of UserDefaults values is currently not supported on Linux " )
118- continuation. finish ( )
119+ print ( " AsyncStream of UserDefaults values is currently not supported on Linux " )
120+ continuation. finish ( )
119121 #endif
120122 }
121123 }
@@ -133,12 +135,12 @@ extension UserDefaults.Dependency: DependencyKey {
133135 // TODO: NSUbiquitousKeyValueStore variant?
134136}
135137
136- private extension UserDefaults {
137- func contains( key: String ) -> Bool {
138+ extension UserDefaults {
139+ fileprivate func contains( key: String ) -> Bool {
138140 self . object ( forKey: key) != nil
139141 }
140142
141- func getSendable( forKey key: String , as type: Any . Type ) -> ( any Sendable ) ? {
143+ fileprivate func getSendable( forKey key: String , as type: Any . Type ) -> ( any Sendable ) ? {
142144 switch type {
143145 case let type where type == Bool . self, let type where type == Bool? . self:
144146 guard self . contains ( key: key) else { return nil }
@@ -162,7 +164,7 @@ private extension UserDefaults {
162164 }
163165 }
164166
165- func setSendable( _ value: ( any Sendable ) ? , forKey key: String ) {
167+ fileprivate func setSendable( _ value: ( any Sendable ) ? , forKey key: String ) {
166168 guard let value = value else {
167169 self . removeObject ( forKey: key)
168170 return
@@ -188,64 +190,64 @@ private extension UserDefaults {
188190 }
189191}
190192#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
191- @available ( iOS 5 . 0 , tvOS 9 . 0 , macOS 10 . 7 , watchOS 9 . 0 , * )
192- private extension NSUbiquitousKeyValueStore {
193- func contains( key: String ) -> Bool {
194- self . object ( forKey: key) != nil
195- }
196-
197- func getSendable( forKey key: String , as type: Any . Type ) -> ( any Sendable ) ? {
198- switch type {
199- case let type where type == Bool . self, let type where type == Bool? . self:
200- guard self . contains ( key: key) else { return nil }
201- return self . bool ( forKey: key)
202- case let type where type == Data . self, let type where type == Data? . self:
203- return self . data ( forKey: key)
204- case let type where type == Date . self, let type where type == Date? . self:
205- return self . object ( forKey: key) as? Date
206- case let type where type == Double . self, let type where type == Double ? . self:
207- guard self . contains ( key: key) else { return nil }
208- return self . double ( forKey: key)
209- case let type where type == Int . self, let type where type == Int? . self:
210- guard self . contains ( key: key) else { return nil }
211- return Int ( self . longLong ( forKey: key) )
212- case let type where type == String . self, let type where type == String ? . self:
213- return self . string ( forKey: key)
214- case let type where type == URL . self, let type where type == URL? . self:
215- // TODO: Improve to handle file urls
216- guard let string = self . string ( forKey: key) else { return nil }
217- return URL ( string: string)
218- default :
219- return nil
193+ @available ( iOS 5 . 0 , tvOS 9 . 0 , macOS 10 . 7 , watchOS 9 . 0 , * )
194+ extension NSUbiquitousKeyValueStore {
195+ fileprivate func contains( key: String ) -> Bool {
196+ self . object ( forKey: key) != nil
220197 }
221- }
222198
223- func setSendable( _ value: ( any Sendable ) ? , forKey key: String ) {
224- guard let value = value else {
225- self . removeObject ( forKey: key)
226- return
199+ fileprivate func getSendable( forKey key: String , as type: Any . Type ) -> ( any Sendable ) ? {
200+ switch type {
201+ case let type where type == Bool . self, let type where type == Bool? . self:
202+ guard self . contains ( key: key) else { return nil }
203+ return self . bool ( forKey: key)
204+ case let type where type == Data . self, let type where type == Data? . self:
205+ return self . data ( forKey: key)
206+ case let type where type == Date . self, let type where type == Date? . self:
207+ return self . object ( forKey: key) as? Date
208+ case let type where type == Double . self, let type where type == Double ? . self:
209+ guard self . contains ( key: key) else { return nil }
210+ return self . double ( forKey: key)
211+ case let type where type == Int . self, let type where type == Int? . self:
212+ guard self . contains ( key: key) else { return nil }
213+ return Int ( self . longLong ( forKey: key) )
214+ case let type where type == String . self, let type where type == String ? . self:
215+ return self . string ( forKey: key)
216+ case let type where type == URL . self, let type where type == URL? . self:
217+ // TODO: Improve to handle file urls
218+ guard let string = self . string ( forKey: key) else { return nil }
219+ return URL ( string: string)
220+ default :
221+ return nil
222+ }
227223 }
228- switch value {
229- case let value as Bool :
230- self . set ( value, forKey: key)
231- case let value as Data :
232- self . set ( value, forKey: key)
233- case let value as Date :
234- self . set ( value, forKey: key)
235- case let value as Double :
236- self . set ( value, forKey: key)
237- case let value as Int :
238- self . set ( value, forKey: key)
239- case let value as String :
240- self . set ( value, forKey: key)
241- case let value as URL :
242- // TODO: Improve to handle file urls
243- self . set ( value. absoluteString, forKey: key)
244- default :
245- return
224+
225+ fileprivate func setSendable( _ value: ( any Sendable ) ? , forKey key: String ) {
226+ guard let value = value else {
227+ self . removeObject ( forKey: key)
228+ return
229+ }
230+ switch value {
231+ case let value as Bool :
232+ self . set ( value, forKey: key)
233+ case let value as Data :
234+ self . set ( value, forKey: key)
235+ case let value as Date :
236+ self . set ( value, forKey: key)
237+ case let value as Double :
238+ self . set ( value, forKey: key)
239+ case let value as Int :
240+ self . set ( value, forKey: key)
241+ case let value as String :
242+ self . set ( value, forKey: key)
243+ case let value as URL :
244+ // TODO: Improve to handle file urls
245+ self . set ( value. absoluteString, forKey: key)
246+ default :
247+ return
248+ }
246249 }
247250 }
248- }
249251#endif
250252extension UserDefaults . Dependency : TestDependencyKey {
251253 public static let testValue : Self = {
@@ -295,7 +297,7 @@ extension UserDefaults.Dependency: TestDependencyKey {
295297 }
296298}
297299
298- fileprivate func _isEqual( _ lhs: ( any Sendable ) ? , _ rhs: ( any Sendable ) ? ) -> Bool {
300+ private func _isEqual( _ lhs: ( any Sendable ) ? , _ rhs: ( any Sendable ) ? ) -> Bool {
299301 switch ( lhs, rhs) {
300302 case let ( . some( lhs) , . some( rhs) ) :
301303 return ( lhs as! any Equatable ) . isEqual ( other: rhs)
@@ -312,73 +314,74 @@ extension Equatable {
312314}
313315
314316#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
315- extension UserDefaults . Dependency {
316- /// An iCloud-based container of key-value pairs you use to share data among
317- /// instances of your app running on a user's connected devices.
318- @available ( iOS 5 . 0 , tvOS 9 . 0 , macOS 10 . 7 , watchOS 9 . 0 , * )
319- public static var ubiquitous : UserDefaults . Dependency {
320- let store = NSUbiquitousKeyValueStore . default
321- let userDefaults = UncheckedSendable ( store)
322- let subject = AsyncSharedSubject < ( String , any Sendable ) > ( )
317+ extension UserDefaults . Dependency {
318+ /// An iCloud-based container of key-value pairs you use to share data among
319+ /// instances of your app running on a user's connected devices.
320+ @available ( iOS 5 . 0 , tvOS 9 . 0 , macOS 10 . 7 , watchOS 9 . 0 , * )
321+ public static var ubiquitous : UserDefaults . Dependency {
322+ let store = NSUbiquitousKeyValueStore . default
323+ let userDefaults = UncheckedSendable ( store)
324+ let subject = AsyncSharedSubject < ( String , any Sendable ) > ( )
323325
324- return UserDefaults . Dependency { key, type in
325- userDefaults. value. getSendable ( forKey: key, as: type)
326- } set: {
327- userDefaults. value. setSendable ( $0, forKey: $1)
328- // NSUbiquitousKeyValueStore doesn't support KVO, so we call directly
329- // the continuation for local changes.
330- subject. yield ( ( $1, $0) )
331- userDefaults. value. synchronize ( )
332- } values: { key, type in
333- AsyncStream ( ( any Sendable ) ? . self) { continuation in
334- final class Observer : NSObject , Sendable {
335- let key : String
336- let type : Any . Type
337- let onChange : @Sendable ( ( any Sendable ) ? ) -> Void
338- init (
339- key: String ,
340- type: Any . Type ,
341- onChange: @escaping @Sendable ( ( any Sendable ) ? ) -> Void
342- ) {
343- self . key = key
344- self . type = type
345- self . onChange = onChange
346- super. init ( )
347- NotificationCenter . default. addObserver (
348- self , selector: #selector( onNotification) ,
349- name: NSUbiquitousKeyValueStore . didChangeExternallyNotification,
350- object: NSUbiquitousKeyValueStore . default
351- )
352- }
326+ return UserDefaults . Dependency { key, type in
327+ userDefaults. value. getSendable ( forKey: key, as: type)
328+ } set: {
329+ userDefaults. value. setSendable ( $0, forKey: $1)
330+ // NSUbiquitousKeyValueStore doesn't support KVO, so we call directly
331+ // the continuation for local changes.
332+ subject. yield ( ( $1, $0) )
333+ userDefaults. value. synchronize ( )
334+ } values: { key, type in
335+ AsyncStream ( ( any Sendable ) ? . self) { continuation in
336+ final class Observer : NSObject , Sendable {
337+ let key : String
338+ let type : Any . Type
339+ let onChange : @Sendable ( ( any Sendable ) ? ) -> Void
340+ init (
341+ key: String ,
342+ type: Any . Type ,
343+ onChange: @escaping @Sendable ( ( any Sendable ) ? ) -> Void
344+ ) {
345+ self . key = key
346+ self . type = type
347+ self . onChange = onChange
348+ super. init ( )
349+ NotificationCenter . default. addObserver (
350+ self , selector: #selector( onNotification) ,
351+ name: NSUbiquitousKeyValueStore . didChangeExternallyNotification,
352+ object: NSUbiquitousKeyValueStore . default
353+ )
354+ }
353355
354- @objc func onNotification( _ notification: Notification ) {
355- guard
356- let store = notification. object as? NSUbiquitousKeyValueStore ,
357- let keys = notification. userInfo ? [ NSUbiquitousKeyValueStoreChangedKeysKey] as? [ String ] ,
358- keys. contains ( key)
359- else { return }
360- self . onChange ( store. getSendable ( forKey: self . key, as: self . type) )
356+ @objc func onNotification( _ notification: Notification ) {
357+ guard
358+ let store = notification. object as? NSUbiquitousKeyValueStore ,
359+ let keys = notification. userInfo ? [ NSUbiquitousKeyValueStoreChangedKeysKey]
360+ as? [ String ] ,
361+ keys. contains ( key)
362+ else { return }
363+ self . onChange ( store. getSendable ( forKey: self . key, as: self . type) )
364+ }
361365 }
362- }
363366
364- let object = Observer ( key: key, type: type) {
365- subject. yield ( ( key, $0) )
366- }
367+ let object = Observer ( key: key, type: type) {
368+ subject. yield ( ( key, $0) )
369+ }
367370
368- let task = Task {
369- for await tuple in subject. stream ( bufferingPolicy: . bufferingNewest( 0 ) ) {
370- if tuple. 0 == key {
371- continuation. yield ( tuple. 1 )
371+ let task = Task {
372+ for await tuple in subject. stream ( bufferingPolicy: . bufferingNewest( 0 ) ) {
373+ if tuple. 0 == key {
374+ continuation. yield ( tuple. 1 )
375+ }
372376 }
373377 }
374- }
375378
376- continuation. onTermination = { [ object] _ in
377- task. cancel ( )
378- let _ = object
379+ continuation. onTermination = { [ object] _ in
380+ task. cancel ( )
381+ let _ = object
382+ }
379383 }
380384 }
381385 }
382386 }
383- }
384387#endif
0 commit comments