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?

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.

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.

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"

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.

@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.

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

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?

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... :-)

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?

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.

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:)?

Lost data in UserDefaults
 
 
Q