Lost data in UserDefaults

My app user sometimes lost data in UserDefaults when our app launched.

All or part of the data is lost.
A device that has happened once is likely to happen again.
This bug seems to have been around since October 2020.
Not depend on any OS or hardware, and we haven’t been able to reproduce it yet.

Does anyone face the same bug?

Post not yet marked as solved Up vote post of Shizunait Down vote post of Shizunait
14k views

Replies

One of our apps is facing the same issue. We've seen a huge spike in the behavior you've described after the iOS 15 launch. Before that, only a small subset of users had problems with that.

Sadly, we were unable to find the root cause of the issue. Our temporary solution was to completely replace UserDefaults with our own implementation.

  • @claps lock, did you use a public repo for this or completely wrote yourself from scratch?

Add a Comment

I'm also experiencing this same issue using UserDefaults with iOS 15. It only happens to some users and it's not easily reproducible. I'm not sure what to do because my original implementation involved writing a custom file in plist format that also stopped working reliably with iOS 15.

  • OK, I've now been able to debug it so far as to know that the keys are still there, but their values have been read in as 0/NO (when integer or boolean), and some scrambled undecodable bytes when an object. I'm guessing that they were written that way.

  • @juggleware Could you give some tips on reproducing and debugging this?

  • I wasn't able to find any easy way to debug it. I have my own debug log wrapper that I used that holds debug text in a string, and the beta tester can use a button in-app to send it to me so that I could confirm that keys in UserDefaults were being seen but they were being read in as 0s in  application(_:didFinishLaunchingWithOptions:). I have gone back to writing my own prefs file, but this time keeping multiple backups of the file in case of corruption. I'm not sure yet if this will fix the issue as I'm still beta testing, but I'm explicitly writing the backup files with no data protection as there is no private data in there.

Add a Comment

For folks hitting this issue starting with iOS 15…

First up, read Prepare Your App for Prewarming. This explain an important change in behaviour in iOS 15 that could trigger issues like this.

And now my questions: Does your app have the Data Protection Entitlement entitlement (com.apple.developer.default-data-protection) set? If so, what to?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • Thank you so much. This fixed our issue. I'll make sure to link your answer in all existing threads, as there are a few of them.

    More info: In one of our apps, in application(_:didFinishLaunchingWithOptions:), based on what flags in UserDefaults are set, we either present the main dashboard OR do a complete cleanup (UserDefaults + Keychain) and present the login screen. In our case, Data Protection Entitlement was indeed set to NSFileProtectionComplete years ago. This, combined with Prewarming caused the issue in our case.

  • Could you elaborate what you changed?

    Furthermore it sounds like you were accessing UserDefaults in application(_:didFinishLaunchingWithOptions:) and had the issue. But when my team and I read the prewarming documentation, it feels like the application shouldn't even get to application(_:didFinishLaunchingWithOptions:)

  • @claps lock I forgot to tag you on my last comment. Do you mind reading it over? We have a business requirement for NSFileProtectionComplete, and we can't simply remove this protection.

    Also, are you able to reproduce consistently? Our one dev was only able to once, and we are still seeing the issue with users.

I believe we have users experiencing issues due to UserDefaults.standard not providing the expected values. Our app does have data protection set to Complete.

We are accessing UseDefaults func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool . I am unclear if this is safe or not. Based on prepare your app for prewarming, I feel that we are far enough along that we should be okay?

We have a business requirement for NSFileProtectionComplete

My general advice is that you not do this. It’s just too easy to get yourself into trouble that way. Rather, you should use a less restrictive default file protection and then set a more restrictive file protection on files that contain user data that you want to protect. That way you can coordinate the file protection and the code that accesses those files.

If you ignore that advice and stick with NSFileProtectionComplete as the default, it’s best to stop using NSUserDefaults for anything meaningful. That API does not give control over when it reads and writes the disk, and thus there’s no way to coordinate its file system access with your restrictive file protection.

IMPORTANT All of the above is based on the assumption that your app can run in the background. If you’re absolutely certain that your app will never run in the background, using NSFileProtectionComplete as the default file protection is just fine.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Could you elaborate on what you changed?

We've changed the Data Protection Entitlement to NSFileProtectionCompleteUntilFirstUserAuthentication which solved the issue for us.

Also, are you able to reproduce consistently?

Yes, it's easily reproducible. Reproduction steps in our case:

  1. Launch the app and log in.
  2. Force quit the app.
  3. Wait.

Somewhere in the next hour or so the prewarming will start, which in resolution will call application(_:didFinishLaunchingWithOptions:). As mentioned previously, in our case, we check for some UserDefaults flags there. If they do not exist then we do a complete cleanup (UserDefaults + Keychain). This caused users on iOS 15 to get logged out.

  • I have NSFileProtectionComplete but can't reproduce the problem in my app, despite many user complaints. Do you have background fetch or any other background related capability enabled for your app?

  • We face a similar issue in iOS 16 versions and also for some iOS 15 users from production records, but we are unable to replicate this issue in our environment even in TestFlight. @claps lock we tried the same steps as you mentioned but had no luck in reproducing this. Do you have any example application where the issue can be reproducible?

Add a Comment

@eskimo could you please clarify what's going on. As @claps lock said, the app prewarm calls application(_:didFinishLaunchingWithOptions:). But as we can see in the documentation, the app prewarming SHOULDN'T call application(_:didFinishLaunchingWithOptions:). What's real?

@surik92 and anyone else still struggling with this issue.

I was able to get some a response from Code Level Tech Support on this. I detailed our problems and referenced this post. I then asked for clarity.

We would love to get clarity here. We've read the documentation many times and it is our understanding that during prewarming the app should NOT execute the AppDelegate function application(_:didFinishLaunchingWithOptions:). If that is true, we shouldn't be seeing the issue given we only access UserDefaults in this method or later on in the life cycle. It appears (see forum post) we are not alone in this issue arising in iOS 15 while accessing Userdefaults in application(_:didFinishLaunchingWithOptions:).

Here is the response I received.

I can tell you from working with many Developers on this issue that if your app is getting pre-warmed then application(_:didFinishLaunchingWithOptions:) IS getting run.  Now, this may be a bug or it may be intended behavior, the jury is still out on that item, but if you feel this is incorrect, I would open a bug report:

Based on this response our team has started to update our code to account for this. We will also be filing a bug with apple as the documentation and actual behavior, now confirmed by apple, are contradicting one another.

  • Wow! Thank you so much! 🖤

  • Did you get any feedback on your bug?

Add a Comment

Is there any way to tell that application(_:didFinishLaunchingWithOptions:) was called during prewarming hence the UserDefaults nor Keychain are available?

Add a Comment

I have a mature app - more than ten years on App Store - that reads a file containing user data, using NSFilemanager during UIApplication *)application didFinishLaunchingWithOptions. In the last few months, I've had several users who claim their data just disappears. I've made no changes to any code that touches the data, so I was perplexed.

This seems a likely culprit. Anyone else experiencing lost data from a file that is read during didFinishLaunchingWithOptions? I imagine that if the prewarm trick calls didFinish..., and the file system is not available, then my code assumes it's a first run and creates an empty user data file. Yikes.

Any idea of how to get this fixed?

  • Is your Data Entitlement Protection level set to NSFileProtectionComplete?

  • I thought I was reading my own comment at first because I had the exact same thing happen with my mature app (also 10 years, but updated once or twice a year). Data corrupted reading during didFinishLaunchingWithOptions most likely due to prewarming. I was not using NSFileProtectionComplete, either. The fix that "worked" for me was recommended by an Apple engineer: constantly write backup files of your settings (I'm keeping 10) and if one is unreadable, go back to the next most recent. Shamefully ugly, but I've had no new user complaints since.

Add a Comment

Similar issue here, as we have a mature application running already for several years. After iOS 15 was released some first reports came in that the prescriber (medical user) needed to recertify that (s)he was allowed to prescribe medicine. Once completed the settings are stored in the standardUserDefaults.

- (instancetype)init {
  self = [super init];
  if(!self) return nil;
  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];

   _name  = [ud stringForKey: kName];
... 

 isDirty = NO;
   
  return self;
}

During the didFinishLaunchingWithOptions function there are settings loaded for reporting amongst others which call for retrieving the standardUserDefaults.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [[IQKeyboardManager sharedManager] setEnable:YES];

   [self xxxx:launchOptions];

   return YES;
}

Following then the execution path it eventually end up at:

[xxxx sessionProperties:[Settings userData]]; 

which then calls the init function containing the [NSUserDefaults standardUserDefaults];

To answer eskimo's question:

And now my questions: Does your app have the Data Protection Entitlement entitlement (com.apple.developer.default-data-protection) set? If so, what to?

Yes, NSFileProtectionComplete is configured.

Now the question arises where to move the app initialization functions to... :-)

  • "Now the question arises where to move the app initialization functions to" - this is also a question we would like to have answered. We are currently experiencing the same thing.

    Rx-Whizz did you get to solve this somehow?

    Eskimo - any answer to this????

Add a Comment

There is also a follow up question which is probably more important; How can this be debugged? How to configure XCode with breakpoints to see how the prewarm is taking place and which calls it makes?

  • Totally agree with this. How can you simulate prewarming in Xcode. Furthermore, if this feature is not yet available in Xcode, if one makes recommended refactoring of lifecycle methods, how can we gain confidence using the app for weeks on end even executes a prewarming call?

Add a Comment

I'm still getting user reports of missing data and feel pretty certain this is the issue - though I still can't reproduce reliably.

Some longtime users are having their data completely disappear with no new usage patterns, which causes them to get pretty upset with me - the only other entity they imagine could be at fault.

Can anyone relay reliable guidance on whether or not this is a bug or a feature? I'm at a standstill with several agitated users.

This happened to us right when iOS 15 initially rolled out and we had many complaints from users who had to re-login. (oh the horror!) At first I wasn't sure what was going on but I think this is what was plaguing us. We haven't gotten any complaints recently so maybe this feature/bug got fixed.

  • I'm guessing Apple fixed this behind the scenes. Can anyone confirm this? @eskimo

  • I had a user report the problem after updating to iOS 15.3My fingers are crossed today's iOS 15.3.1 update has a fix. Of course, my fingers have been crossed for months.

  • we are still seeing this issue in 15.3

Our customers are complaining about this and this seems to be happening fairly frequently. We do not use Data Protection Entitlement, so I am not sure why User Defaults are getting wiped or not available in application(_:didFinishLaunchingWithOptions:)?

  • What iOS are your customers on when they experience this bug? I've had only a few reports of missing data in the past few weeks so I'd hoped it was fixed in 15.3.1 Without guidance from Apple, it's a guessing game. But the more info we can share with each other, the better our guesses can be.

  • @davenorfleet I just learned today that the devices were on 15.2 instead of 15.3.1 which I was told earlier. Hopefully, they won't see the issue again on 15.3.1. It was happening on iPhone 12 pros. It's still a guessing game as we are not able to get a straight answer from Apple. We are now thinking of having a backup of User Defaults saved in a file which possibly can be applied as a restore option if this ever happens again. Our apps are matured ones that rely heavily on User Defaults. As we are not able to reproduce this, I am not even sure what all is wiped, is it just user defaults, is it also application support directory, its all a guessing game.

  • So far, I've had no user reports of this happening after iOS 15.4 update. Has anyone else had the bug reported from a user on 15.4? I hope it's been fixed, and REALLY wish Apple would start acknowledging their open bugs. I want to be compassionate regarding possible workplace slowdowns, but without updates about what's going on, I get the sinking feeling that a bunch of Apple folk are staying home in their PJs ;-)

Add a Comment