IHaveAQuestion IHaveAQuestion - 11 days ago 6
Swift Question

UserDefaults being read incorrectly sometimes

I have been developing an app that stores user data (username, etc.) between launches of the app; I stored this data in UserDefaults. However, I have recently noticed a problem: Sometimes, when I run the app, I will get some value back from

UserDefaults.standard.object(forKey:)
, but other times I will get
nil
when I know for a fact that there is something there.

This makes no sense to me.

I have searched for the answer to this question on SO but only found this, which did not help.

In order to test this, I made an empty app and put the following in
viewDidAppear
, and then I ran the app once:

UserDefaults.standard.setValue("aValue", forKey: "aKey")


The above line is just to ensure that there is in fact a value stored. I then deleted the above line from
viewDidAppear
, and then I put this in
didFinishLaunchingWithOptions
:

print(UserDefaults.standard.object(forKey: "aKey") as? String)


I then ran the app 20 times. Here is my data for if the
print(...)
printed
nil
or
"aValue"
:

Answer

It is crazy to put the set in viewDidAppear but the get in didFinishLaunching, because they are unrelated and you don't know anything about the order in which they will happen or even whether viewDidAppear will ever happen (not every view controller ever appears, after all).

Put them in the same place to run your test. For example, let's put them both in didFinishLaunching:

let val = UserDefaults.standard.object(forKey: "aKey") as? String
if val != nil {
    print("got it")
} else {
    print("didn't get it, setting it")
    UserDefaults.standard.set("aValue", forKey: "aKey")
}

You'll find that this works just as you would expect.

EDIT Okay, thanks to the movie you posted, we see that there's a complication: you're testing this on the device. You are testing by repeatedly hitting the Run button in Xcode without stopping the app in a coherent way, and this possibly confuses Xcode so that it does a complete replacement, or so that it never gets a chance to write the defaults to disk in the first place. Instead, Run, switch to the device and hit the Home button, now switch back to Xcode and Stop. Now Run again. I think you'll find that you'll get a more consistent result.