Reputation: 19767
I use NSUserDefaults
to persist approximately 100 key/value pairs on the user's iOS device. Each pair is just a string key and boolean value. This works fine almost all the time. Lately, a few users mentioned their app being 'reset'. Specifically, their app was not correctly reading data from NSUserDefaults
. I'm trying to understand how this can happen.
A few things to note:
synchronize
after every update to the user defaultsapplication:didFinishLaunchingWithOptions:
I found some interesting comments in this Loom.com blog post. Sounds like NSUserDefaults
backing plist might be inaccessible when the app is restarted in the background. I'm not sure if backgrounded apps are restarted if they crash while in the background. However, I'm curious because my app is indeed crashing in the background according to my crash reporting service. Also, this crash happens immediately after receiving a memory warning.
Is it possible for the app to fail reading the user defaults when restarted in the background after a crash (while in the background)?
Any advice on how to diagnose this issue is greatly appreciated!
Edit - more info: sounds like the CoreLocation framework can cause apps to restart in the background after crashing in the background. My app includes a few 3rd party advertising and analytics SDKs. In fact, this problem started to show up after the addition of one particular SDK which could use CoreLocation.
Upvotes: 3
Views: 1362
Reputation: 1201
I think using NSUserDefaults
to save 100 key/value is a wrong approach.
But if you use NSUserDefaults
to save this 100 key/value and you read The defaults in application:didFinishLaunchingWithOptions:
Then you just restore your also read it in
- (void)applicationWillEnterForeground:(UIApplication *)application;
Upvotes: 0
Reputation: 26383
I think that using NSUserDefaults
to save 100 key/data is probably wrong in in the first place from a development view.
The most right way is save login/sesistive data in the keychain (where you have encryption for free) and the rest in documents dir using a serialized plist. In the NSUserDefaults
I would save something like the path of the preferences file or the version number of the preference file.
Your app can be restarted after a crash in background if there is a trigger that realauch it such as CoreLocation
or notification.
I really doubt that NSUserDefaults
is not available on background (it also thread safe), I assume it would be a problem so big that SO will have plenty of similar questions.
The fact you are receiving a memory warning on the background is probably related to the cause of crashes. What does the crash log say? can you post it?
What's the memory footprint when your app get suspended? Do you have some methods triggered by memory warning or app lifecycle notifications?
Upvotes: 3
Reputation: 33389
iOS does some complicated stuff to (almost) seamlessly encrypt data written to the disk, so this kind of bug is definitely possible. Perhaps the file cannot be decrypted for some reason and is deleted instead, reverting NSUserDefaults
.
I don't know that is the cause but it seems likely to me.
Also, beware NSUserDefaults
saves data to <Application_Home>/Library
which is not a safe location. It is intended for "files your application downloads or generates and can recreate as needed".
Perhaps a better location to store your data is <Application_Home>/Documents
, which is for data that "cannot be recreated by your app". If your user defaults are important enough for this to be a problem, then it classifies as "user generated content" and should therefore be stored in the Documents folder.
So, I suggest dropping NSUserDefaults
since it does not meet your needs, and save the data by writing an NSDictionary
to the Documents folder, using either NSCoding or Binary Plist (make sure you set it to NSPropertyListBinaryFormat_v1_0
, as that is not the default and should be used on slow flash memory like iOS devices have).
Apple has good documentation and sample code for NSCoding and Plist serialization:
You could also use core data, which is what I'm using in my app, or SQLite. But if you are only storing "hundreds" of settings I wouldn't go with either of these options. They're generally only a good choice if the data does not fit in RAM. For data that does fit in memory NSCoding and Plist are significantly faster and easier to work with.
And also read up on "Where You Should Put Your App‘s files": https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html
Upvotes: 5