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 com.apple.MobileBackup
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 = "com.apple.MobileBackup";
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.
-
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 ↩