sunshinejr / SwiftyUserDefaults

Modern Swift API for NSUserDefaults
http://radex.io/swift/nsuserdefaults/static
MIT License
4.85k stars 365 forks source link

Looks like you can't override Defaults shortcut for property wrapper #251

Closed nikans closed 3 years ago

nikans commented 4 years ago

I have an overriden Defaults shortcut on top level in some settings module, imported both to the main app and custom keyboard extension with shared suite. I wrapped some properties, changes do not propagate to app extension.

sunshinejr commented 3 years ago

This is way overdue, but @nikans that's correct - though you can pass your own adapter in the property wrapper initializer.

spydercapriani commented 3 years ago

though you can pass your own adapter in the property wrapper initializer.

@sunshinejr This suggestion unfortunately also will not work due to the way the propertyWrapper is setup. Take a look at your code for get / set in SwiftyUserDefault class. It is only ever referencing the global Defaults.

public var wrappedValue: T {
        get {
            if options.contains(.cached) {
                return _value ?? Defaults[key: key]
            } else {
                return Defaults[key: key]
            }
        }
        set {
            _value = newValue
            Defaults[key: key] = newValue
        }
    }

   ....

    public init<KeyStore>(keyPath: KeyPath<KeyStore, DefaultsKey<T>>, adapter: DefaultsAdapter<KeyStore>, options: SwiftyUserDefaultOptions = []) {
        self.key = adapter.keyStore[keyPath: keyPath]
        self.options = options

        if options.contains(.observed) {
            observation = adapter.observe(key) { [weak self] update in
                self?._value = update.newValue
            }
        }
    }

You'll notice, the adapter (which houses the UserDefaults) is only being used to fetch the key / observe updates (bug), however writes are not being processed through this adapter in the get set functions. I believe you'll want to store reference to the adapter in SwiftyUserDefault wrapper class and use this for the get set methods.

As result, you could also migrate to just one init where Defaults is passed as the default argument for adapter and ultimately get rid of this init

public init(keyPath: KeyPath<DefaultsKeys, DefaultsKey<T>>, options: SwiftyUserDefaultOptions = []) {
        self.key = Defaults.keyStore[keyPath: keyPath]
        self.options = options

        if options.contains(.observed) {
            observation = Defaults.observe(key) { [weak self] update in
                self?._value = update.newValue
            }
        }
    }