Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UserDefaults being read incorrectly sometimes

Tags:

ios

swift

swift3

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":

✓ 𐄂 𐄂 𐄂 ✓ 𐄂 ✓ ✓ ✓ ✓ ✓ ✓ 𐄂 ✓ ✓ ✓ 𐄂 ✓ ✓ 𐄂

It printed nil 35% of the time and it seems to me fairly random too.

I have two questions:

Why would this happen, and how can I fix/prevent it?

like image 770
IHaveAQuestion Avatar asked Jan 25 '26 11:01

IHaveAQuestion


1 Answers

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.

like image 111
matt Avatar answered Jan 26 '26 23:01

matt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!