Data Protection Entitlements

I would like to implement Data Protection (NSFileProtectionComplete) for all files that our app creates (both normal files as well as the CoreData database file).


If I specify the NSFileProtectionComplete options on creating an NSPersistentStoreCoordinator it works and I can't save anything to the store when in background. But it's a real pain in the *** if you would have to specify NSFileProtectionComplete for every file you create.


I did some reading on the internet and found I had to add the Data Protection capability in the App Target. I did this and saw it was also enabled with the "Complete Protection" in the App ID Settings online. I created new provisioning profiles and installed them. I expected that all files I create are now by default protected using the NSFileProtectionComplete setting, but this does not seems the case. Is this true or do you have to provide the NSFileProtectionComplete setting seperate for every file you create?


Regards.

Replies

The default data protection set in your entitlements (and hence from your App ID via your provisioning profile) is only applied when your app container is created. Try deleting the app from the device and then reinstalling it from scratch. Things should work then.

If not, dump the entitlements of your app to make sure the data protection entitlement made it to your app’s binary correctly. To do this:

  • dump the app entitlements with the following command

    $ codesign -d --entitlements :- /path/to/your.app

  • check for the presence and value of the

    com.apple.developer.default-data-protection
    entitlement



    Share and Enjoy

    Quinn "The Eskimo!"

    Apple Developer Relations, Developer Technical Support, Core OS/Hardware

    let myEmail = "eskimo" + "1" + "@apple.com"

    This doesn't make any sense.


    Again if I specify NSFileProtectionComplete for the persistent store coordinator I get an error on every CRUD action I perform in the background (see code snippet below).


    If I remove that line of code for the persistent store coordinator, the default should kick in which is also NSFileProtectionComplete (specified in entitlement, dump shows this also).


    <dict>
      <key>application-identifier</key>
      <string>xxxxx</string>
      <key>aps-environment</key>
      <string>development</string>
      <key>com.apple.developer.default-data-protection</key>
      <string>NSFileProtectionComplete</string>
      <key>com.apple.developer.team-identifier</key>
      <string>xxxxxxx</string>
      <key>com.apple.security.application-groups</key>
      <array>
      <string>group.com.xxxxxxx</string>
      </array>
      <key>get-task-allow</key>
      <true/>
    </dict>

    A) If I trigger a background fetch right after the app goes to the background all the actions succeed, without any error. If I then lock the device and wait for 10 seconds or more (as specified by the iOS Security Guide -> Complete Protection) all the actions will still succeed. The latter should fail. Besides succeeding background fetches will also succeed.


    B) But if I go to the homescreen from the app -> lock the device -> wait for 10 seconds or more -> trigger a background fetch, the only action that will fail is the delete action (or at least where I will get an error about). This shows at least some error, but the CRU actions still succeed which shouldn't be the case.


    I am using an app group, and the class responsible for creating the persistent store coordinator is located inside a framework. I don't now if this could be a problem. But so far it all seems pretty inconsistent with the documentation.


    The background fetch completion handler:



    AMLog(@"Started");
    
        AMLog(@"Creating");
        AMCDCompany *company = [NSEntityDescription insertNewObjectForEntityForName:@"Company" inManagedObjectContext:[AMDataStore managedObjectContext]];
        [[AMDataStore dataStore] performSaveContext];
        AMLog(@"Created Complete");
      
        AMLog(@"Updating");
        company.name = @"Test";
        [[AMDataStore dataStore] performSaveContext];
        AMLog(@"Update complete");
      
        AMLog(@"Deleting");
        [[AMDataStore managedObjectContext] deleteObject:company];
        [[AMDataStore dataStore] performSaveContext];
        AMLog(@"Done deleting");
      
        AMLog(@"Retrieving");
        NSFetchRequest *fetch = [[NSFetchRequest alloc] initWithEntityName:@"Company"];
        NSError *error;
        NSArray *companies = [[AMDataStore managedObjectContext] executeFetchRequest:fetch error:&error];
        if (error) {
            AMLog(@"Error: %@", error);
        }
        AMLog(@"Done retrieving");
      
        AMLog(@"Ended");
      
        completionHandler(UIBackgroundFetchResultNoData);

    There are two parts to this:

    • what’s happening at the file system layer

    • what’s Core Data doing

    I can really only talk about the former; I’m not a Core Data expert.

    If you want to get to the bottom of this I suggest you run a test with Core Data out of the equation. That is, replace your Core Data CRUD test with a test that hits the file system directly.

    My guess is that you’ll see that the file system layer is behaving as expected, at which point you can start investigating the Core Data side of things. OTOH, if you find unexpected behaviour in the file system, post the details and I can take a look.

    Share and Enjoy

    Quinn "The Eskimo!"
    Apple Developer Relations, Developer Technical Support, Core OS/Hardware

    let myEmail = "eskimo" + "1" + "@apple.com"