I am using a user default values to observe notification preferences. I print the value on app launch:
func application(_ application: UIApplication, didFinishLaunchingWithOptionslaunchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
print("notifications are initially (UserDefaults.standard.bool(forKey: UserDefaults.Keys.notificationsEnabled))")
}
And on app termination:
func applicationWillTerminate(_ application: UIApplication) {
print("notifications are finally (UserDefaults.standard.bool(forKey: UserDefaults.Keys.notificationsEnabled))")
}
This works fine most of the time, but occasionally the value just resets to what it was initially:
notifications are initially false
**update notifications to true then force quit app**
notifications are finally true
**reopen app**
notifications are initially false
I have spent hours debugging this and cannot figure out why. It works several times in a row correctly and will occasionally revert back to this for some unknown reason. I tried setting the UserDefault values on the main thread as well, but that doesn’t seem to change anything. Has anyone seen this bug before? If so, how can I resolve this issue?
2
Answers
As the documentation says:
Thus, for performance reasons
UserDefaults
writes to persistent storage asynchronously. A prompt “force-quit” by the user may terminate the app before that is complete.Theoretically, one can
synchronize
. But, as the docs say, it “is unnecessary and shouldn’t be used”. But if you’re really concerned about the force-quit edge-case scenario, this might mitigate the problem.Alternatively, you may want to simply reconsider whether you should be using “user defaults” at all for this information at all. It is intended for user defaults/preferences. E.g., if a user changes a preference and then promptly force-quits, it is not entirely unreasonable that the change might not apply. Force-quit effectively is the user’s way of saying, “kill this app and everything associated with it.”
If you have some critical model data, then perhaps reconsider whether “user defaults” is the right place to store it. You could write it to persistent storage yourself. You might also consider wrapping it in a
beginBackgroundTask
, to ensure the write completes even if it is in progress as the user leaves the app.You might experience this issue during debug sessions (mostly using simulator) due to the app being terminated by stopping Xcode which is inproper case in app lifecycle and it leads to
NSUserDefaults
are not getting synced.As mentioned above, the
synchronize
is the proper way to persist your data, to prevent data loss you can consider callingsynchronize
inapplicationDidEnterBackground:
(or inUIApplication.didEnterBackgroundNotification
handler)