20

Our app has recently been rejected for violating iOS Data Storage Guidelines about backing up files on iCloud.

I must mark data with the do not back up attribute.

I'm storing all my important data in SQLite databases and my user preferences in UserDefaults.plist. Do I have to mark my database.sqlite files as do no back up?

I asked the reviewer if I were to disable iCloud in my AppID would it help my situation, and the response was cryptic and didn't really give me a yes or no answer.

Nothing in my app really needs to be backed up, can I just disable iCloud support in my AppID and not have to worry about marking files as do not back up??

Here is the reviewer's response:

2.23

We also found that your app does not follow the iOS Data Storage Guidelines, which is required per the App Store Review Guidelines.

In particular, we found that on launch and/or content download, your app stores 4 MB. To check how much data your app is storing:

  • Install and launch your app
  • Go to Settings > iCloud > Storage & Backup > Manage Storage
  • If necessary, tap "Show all apps"
  • Check your app's storage

The iOS Data Storage Guidelines indicate that only content that the user creates using your app, e.g., documents, new files, edits, etc., should be backed up by iCloud.

Temporary files used by your app should only be stored in the /tmp directory; please remember to delete the files stored in this location when the user exits the app.

Data that can be recreated but must persist for proper functioning of your app - or because customers expect it to be available for offline use - should be marked with the "do not back up" attribute. For NSURL objects, add the NSURLIsExcludedFromBackupKey attribute to prevent the corresponding file from being backed up. For CFURLRef objects, use the corresponding kCFURLIsExcludedFromBackupKey attribute.

For more information, please see Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes?.

It is necessary to revise your app to meet the requirements of the iOS Data Storage Guidelines. For discrete code-level questions, you may wish to consult with Apple Developer Technical Support. Please be sure to:

  • include the complete details of your rejection issues
  • prepare any symbolicated crash logs, screenshots, and steps to reproduce the issues for when the DTS engineer follows up.
PaulG
  • 6,920
  • 12
  • 54
  • 98
  • Could you post the reviewer's response? – Evan Mulawski Apr 26 '13 at 16:23
  • I don't think you can just "disable iCloud" anymore. You need to split your data into stuff that the user creates/modifies, stuff that is dynamically downloaded (and can be re-downloaded), stuff that is temporary and can just be tossed, and stuff that comes with the app install (and can be re-installed). Only the first category should be "cloud-enabled", – Hot Licks Apr 26 '13 at 16:49
  • @Hot Licks: In the Certificates, Identifiers & Profiles section of the Provisioning Portal under App IDs, I can set iCloud to enabled or disabled. I would like to know if this would prevent iCloud backups so I don't have to split my files up. Also I can't do this with a Development profile so there's no way to test. – PaulG Apr 26 '13 at 16:52

3 Answers3

32

Basically, it depends on the kind of data you are storing.

If the data can be regenerated (when a user install the app on another device, for instance), then it should not be backed up.

Otherwise, iCloud backup is OK, as the user will expect his data to be available, even on another device.

In the first scenario, you have basically two ways of achieving this...

Either you use NSURL to set the kCFURLIsExcludedFromBackupKey on your files, either you store them in a location that won't be backed-up, like <Application_Home>/Library/Caches. Note that the second solution is the better, IMHO.

For info, kCFURLIsExcludedFromBackupKey can be used this way:

NSURL * fileURL;

fileURL = [ NSURL fileURLWithPath: @"some/file/path" ];

[ fileURL setResourceValue: [ NSNumber numberWithBool: YES ] forKey: NSURLIsExcludedFromBackupKey error: nil ];

For the second scenario, sometimes Apple reviewers think your data can be re-generated, when it's not. Then you'll have to explain why the data has to be backed-up.

Macmade
  • 52,708
  • 13
  • 106
  • 123
  • Thanks a lot for the response. So to disable iCloud 'completely', I would just set my path to the documents directory? Is that a safe assumption? Also I heard that this only works for iOS 5.1 and above. Right now I support 5.0 and above, is there any reason I shouldn't just set the cutoff to 5.1 and use the above code? Thanks again! – PaulG Apr 26 '13 at 17:25
  • That's an option, but I think you shouldn't store that kind of data into the "Documents" folder. This is also the only way if you need to support iOS < 5.1, as NSURL's `setResourceValue:` method is available only since iOS 5.0 and `kCFURLIsExcludedFromBackupKey` is available only since iOS 5.1. – Macmade Apr 26 '13 at 17:29
  • Thanks! If I have to do this, then what's the point of being able to enable or disable iCloud in my App ID? Does that not serve a purpose? – PaulG Apr 26 '13 at 17:33
  • Nothing related to your app supporting iCloud. It's just the complete iOS backup, that can be configured to use iCloud. So simply use one of the two solutions I mentioned. – Macmade Apr 26 '13 at 17:53
  • @Macmade : I am used your first aproach but still rejected by apple. Can you help me on this. – Divya Bhaloidiya Dec 16 '14 at 13:09
3

You can call the following function to pass the path of your "map.sqlite" as NSURL:

NSURL *url = [NSURL fileURLWithPath:yourSQLitePath];
[self addSkipBackupAttributeToItemAtURL:url];
The function is provided in Apple as:

- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);

    NSError *error = nil;
    BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
                              forKey: NSURLIsExcludedFromBackupKey error: &error];
    if(!success){
        NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
    }
    return success;
}
McDowell
  • 107,573
  • 31
  • 204
  • 267
Ved Gupta
  • 86
  • 3
0

Swift 5 version:

func addSkipBackupAttributeTo(url constURL: URL) {
    do {
        // Need a mutable URL to call setResourceValues
        var url = constURL
        var resourceValues = URLResourceValues()
        resourceValues.isExcludedFromBackup = true
        try url.setResourceValues(resourceValues)
    } catch {
        print("Unable to set isExcludedFromBackup on \(constURL)")
    }
}

URLResourceValues keeps track of which values are modified and only writes those out, so it's fine to initialize a new object, set one key and call setResourceValues with it.

stevex
  • 5,589
  • 37
  • 52