Offline storage without iCloud backups

  • Published 10 May 2012

Today I got my first App-rejection1. NRC Media’s In beeld app recently got a major update, which as it turns out didn’t follow Apple’s iOS Data Storage Guidelines. However, Apple’s review team detected this in the minor bugfix update that I submitted after that. The problem was that all photo’s downloaded by the app were backed up into iCloud resulting in an increase of backup size of about 100MB in some cases.

While you can use the ~/Library/Caches directory to store files that can be reproduced (downloaded again or regenerated), iOS can purge this data at any time, crippling offline support for your app. Marco Arment has a nice write-up about the consequences of this behaviour. While his post now contains an update mentioning the change Apple made in iOS 5.0.1 to address this problem, I couldn’t find a post about the way you should implement it, so here it is.

In iOS 5.0.1 Apple introduced a new file attribute allowing developers to specify which files shouldn’t be backed up. They defined four ‘Data Handling Categories’ described in Technical Q&A QA1719: Critical Data, Cached Data, Temporary Data and Offline Data. Offline Data is defined as “[…] data that can be downloaded or otherwise recreated, but that the user expects to be reliably available when offline”.

Offline data should be stored in the ~/Documents directory or in a subdirectory of ~/Libary such as ~/Library/Private Documents and marked with a special file attribute that specifies that the file should not be backupped, but isn’t purged when storage runs low. How this attribute should be set is different for iOS version 5.0.1 and 5.1.

In iOS 5.0.1 the attribute named should be given the value 1 using setxattr for every filepath that should not be backed up. This attribute is ignored by iOS version 5.0 an lower, so it can be safely added in any iOS version. Sample code for this is provided by Apple:

#include <sys/xattr.h>
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
    const char* filePath = [[URL path] fileSystemRepresentation];

    const char* attrName = "";
    u_int8_t attrValue = 1;

    int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
    return result == 0;

In iOS 5.1 Apple added the NSURLIsExcludedFromBackupKey that can be added to a file URL with the setResourceValue:forKey:error: instance method of NSURL.

These two methods are combined nicely in the following gist:

There is just one caveat: because of a bug in the iOS 5.0 simulator, &NSURLIsExcludedFromBackupKey == nil will return false even if that symbol is not defined. This will cause this piece of code to crash in the iOS 5.0 simulator, but it will run fine in the iOS 5.1 simulator and on any iOS device.

  1. Well, that is not entirely true. My first iOS app, an iPhone client Fetishwijzer got rejected and never made it to the App Store, but I kind of expected that