import Foundation
import Combine
//
// we want to subscribe to any changes in user defaults
//
public class UserDefaultWatcher : NSObject
{
//
// the particular suite of defaults we are watching
//
private let _source : UserDefaults
//
// all key paths that we have asked to observe
//
private var _knownSubscriptions = Set<String>()
//
// we throw all work across to this queue to prevent any threading issues
//
private let _queue = DispatchQueue(label: "user_default_watcher")
//
// our subscription to userDefaults.didChangenotification
//
private var _subscription : AnyCancellable? = nil
//
// we expose this as the result
//
private let _subject = PassthroughSubject<(String,Any), Error>()
//
// the instance of this
//
public static let instance = UserDefaultWatcher( source: UserDefaults.standard )
//
// something we can subscribe to
//
public var values : any Subject<(String,Any),Error> {
return _subject
}
//
// this is what it's all about... here we send out the change to anyone listening
//
private func sendChange( key: String, value: Any )
{
_subject.send((key, value))
}
//
// setup observers for everything - this is called a lot, so we only want to setup observers for
// new variables
//
private func setupObservers( sendEvents : Bool)
{
for (key, value) in _source.dictionaryRepresentation()
{
if (key.starts(with: "NS") || key.starts(with: "Apple"))
{
continue
}
if !_knownSubscriptions.contains(key)
{
_knownSubscriptions.insert(key)
UserDefaults.standard.addObserver(self, forKeyPath: key, context: nil)
if sendEvents
{
sendChange( key: key, value: value )
}
}
}
}
//
// when this is initialized, we just subscribe to all the variables in userdefauls
//
public init( source : UserDefaults)
{
_source = source
super.init()
self._subscription = NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification).sink
{
notification in
self._queue.async
{
self.setupObservers(sendEvents: true)
}
}
_queue.async
{
self.setupObservers( sendEvents: false )
}
}
//
// when we get an event saying one of the keys has changed, we just send it off as a change
//
override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
_queue.async
{
if let key = keyPath, let value = self._source.dictionaryRepresentation()[key]
{
self.sendChange(key: key, value: value )
}
}
}
//
// clean up all the subscriptions
//
deinit
{
_subscription?.cancel()
for keyPath in _knownSubscriptions
{
UserDefaults.standard.removeObserver(self, forKeyPath: keyPath)
}
}
}
1条答案
按热度按时间rdrgkggo1#
所以在@HangarRash提到didChangeNotification之后a-我最终得到了这个,它实际上完全可以工作--但是代码量很大--一定有更好的方法:
所以我可以这样使用它: