sunshinejr / SwiftyUserDefaults

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

Storing nil in String? results in "" #162

Closed mvoelkl closed 5 years ago

mvoelkl commented 6 years ago

This is my definition, please note the Optional type:

extension DefaultsKeys {
    static let myValue = DefaultsKey<String?>("myValue")
}

And this is how I'm storing my value:

Defaults[.myValue] = newValue

And this is me talking to llvm:

(lldb) po newValue
nil

(lldb) po Defaults[.myValue]
▿ Optional<String>
  - some : ""

Expected result: Defaults[.myValue] is nil Version: 4.0.0-alpha.1 Installed via CocoaPods

rromanchuk commented 6 years ago

can confirm this happening, this is quite a dangerous bug

Below the branch always evaluates as true

Defaults[.authToken] = nil
if let authToken = Defaults[.authToken] {

}
quangpc-zz commented 5 years ago

I can see this issue too Version: 4.0.0-alpha.1

farzadshbfn commented 5 years ago

It's not a bug, you didn't define the .authToken correctly. DefaultsKeys are default value provider and in String and String? case, it's "". You can define your key like this:

extension DefaultsKeys {
    static let authToken = DefaultsKey<String?>("authToken", defaultValue: nil)
}
rromanchuk commented 5 years ago

@farzadshbfn no, it is a bug, trust me.

static let authToken = DefaultsKey<String?>("authToken", defaultValue: nil)

this returns ""

farzadshbfn commented 5 years ago

Well that's weird, because I "just" checked it before posting it 🤔

quangpc-vn commented 5 years ago

what version u used for testing @farzadshbfn . We use 4.0.0-alpha.1

farzadshbfn commented 5 years ago

4.0.0-alpha.1, that's why I'm shocked! although I should mention, I'm not using default Defaults value, I used UserDefaults with custom suiteName.

z3bi commented 5 years ago

I experienced the same issue and manually specifying the defaultValue of nil did fix it. But digging into it I'm not sure why that's the case.

Without specifying the defaultValue, the convenience init should be calling the private init which sets the defaultValue to nil

    private init(key: String) {
        self._key = key
        self.defaultValue = nil
    }

so functionally both inits should be equivalent.

Further, the subscript function uses the nil coalescing operator to run through possible return values

return T.get(key: key._key, userDefaults: self) ?? key.defaultValue ?? T.defaultValue

the T.defaultValue is the blank string defined in the String extension

extension String: DefaultsSerializable, DefaultsDefaultArrayValueType, DefaultsDefaultValueType {

    public static var defaultValue: String = ""

So I'm not sure why the nil coalescing would ever return the specified nil defaultValue instead of the empty string.

sunshinejr commented 5 years ago

Hey guys. Indeed this is a bug. The problem came from the new "default values" protocols. We'll be removing them in a new alpha. Sorry for that.