iCloud and Core Data pre-filled database - ios

I have an app with a pre-filled .sqlite file that is copied into the user's Documents directory when the app is first opened. This file is 12.9MB. Twice now, my app has been rejected since changing target to iOS5 with this rejection note:
Binary Rejected Apr 24, 2012 10:12 AM
Reasons for Rejection:
2.23 Apps must follow the iOS Data Storage Guidelines or they will be rejected
Apr 24, 2012 10:12 AM. From Apple.
2.23
We 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 content download, your app stores 12.81 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., may be stored in the /Documents directory - and 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.
I have tried setting the "do not back up" attribute as recommended in the Data Storage Guidelines, but is was rejected again.
I do not use iCloud in my app, and Settings > iCloud > etc. shows no usage at all.
I cannot use the Caches or tmp directories as the database is modified by the user after creation.
I seem to be between a rock and a hard place here with Apple not allowing this kind of app to function at all.
Has anyone had this problem and managed to overcome it?
EDIT 17-5-12
I still haven't managed to get this app approved yet. Has anyone managed to do this?
EDIT 1-7-12
My app has just been rejected again for the same reason. I am at a loss as to what to do here, as surely it is a common use scenario.
EDIT 11-9-12
App now approved - please see my solution below. I hope it can help someone else.

OK, here is the solution I managed to get approved (finally!)
This is the code for setting the Skip Backup attribute - note that it is different for 5.0.1 and below and 5.1 and above.
#include <sys/xattr.h>
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
if (&NSURLIsExcludedFromBackupKey == nil) { // iOS <= 5.0.1
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;
} else { // iOS >= 5.1
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
}
And here is my persistentStoreCoordinator
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"store.sqlite"];
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent:#"store.sqlite"];
// For iOS 5.0 - store in Caches and just put up with purging
// Users should be on at least 5.0.1 anyway
if ([[[UIDevice currentDevice] systemVersion] isEqualToString:#"5.0"]) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
NSString *oldStorePath = [storePath copy];
storePath = [cacheDirectory stringByAppendingPathComponent:#"store.sqlite"];
storeURL = [NSURL URLWithString:storePath];
// Copy existing file
if ([fileManager fileExistsAtPath:oldStorePath]) {
[fileManager copyItemAtPath:oldStorePath toPath:storePath error:NULL];
[fileManager removeItemAtPath:oldStorePath error:NULL];
}
}
// END iOS 5.0
if (![fileManager fileExistsAtPath:storePath]) {
// File doesn't exist - copy it over
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"store" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[self addSkipBackupAttributeToItemAtURL:storeURL];
return __persistentStoreCoordinator;
}
Note that I made the decision to just store in Caches and put up with purging for iOS 5.0 users.
This was approved by Apple this month.
Please don't copy and paste this code without reading and understanding it first - it may not be totally accurate or optimised, but I hope it can guide someone to a solution that helps them.

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#include <sys/xattr.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Put this in your method
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSURL *pathURL= [NSURL fileURLWithPath:documentsDirectory];
iOS5 = NO;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"5.0.1")) {
iOS5 = YES;
}
// Set do not backup attribute to whole folder
if (iOS5) {
BOOL success = [self addSkipBackupAttributeToItemAtURL:pathURL];
if (success)
NSLog(#"Marked %#", pathURL);
else
NSLog(#"Can't marked %#", pathURL);
}
}
- (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;
}

You said that you don't use iCloud. In that case, you should simply move your sqlite file to a directory with suffix .nosync. That should do it!
NSString *dataFileName = #"my.sqlite";
NSString *dataFileDirectoryName = #"Data.nosync";
NSString *documentsDirectoryPath = [self applicationDocumentsDirectory];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:[documentsDirectoryPath stringByAppendingPathComponent:dataFileDirectoryName]] == NO) {
NSError *fileSystemError;
[fileManager createDirectoryAtPath:[documentsDirectoryPath stringByAppendingPathComponent:dataFileDirectoryName]
withIntermediateDirectories:YES
attributes:nil
error:&fileSystemError];
if (fileSystemError != nil) {
NSLog(#"Error creating database directory %#", fileSystemError);
}
}
NSString *dataFilePath = [[documentsDirectoryPath
stringByAppendingPathComponent:dataFileDirectoryName]
stringByAppendingPathComponent:dataFileName];
// Move your file at dataFilePath location!
HTH.

I've been looking into this and have found this very interesting article on the subject : http://iphoneincubator.com/blog/data-management/local-file-storage-in-ios-5
I believe that you will continue to be rejected if you try to use the /Documents folder to store your DB file.
I would suggest you bite the bullet and use /Cache. The worst user case scenario would be that their device runs low on memory and the app gets cleaned removing the DB file. In this case when your app launches again it should copy over the bundled DB file into /Cache and the user would sync to go and grab the excess data from your remote server. That is assuming this is how your app works.
To get your app to move your DB file from /Documents to /Cache you can just tell NSFileManager to do this for you ...
#define FILE_MANAGER [NSFileManager defaultManager]
#define DOCUMENTS_PATH [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0]
#define CACHES_PATH [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex: 0]
#define DB_DOCS_PATH [DOCUMENTS_PATH stringByAppendingPathComponent: #"database.db"]
#define DB_PATH [CACHES_PATH stringByAppendingPathComponent: #"database.db"]
if([FILE_MANAGER moveItemAtPath: DB_DOCS_PATH toPath:DB_PATH error:&error])
{
NSLog(#"SUCCESSFULLY MOVED %# to %#",DB_DOCS_PATH,DB_PATH);
}
This will prevent your existing users from using their iCloud storage unnecessarily.

Related

App rejected due to data storeage

My app was rejected cause it must follow the iOS Data Storage Guidelines. I have already read some answer here on stackoverflow, and i have already read some blogs... I know my problem, at first application launch i download unzip sqlite db file from server and zip it ,after that i remove unzip file from temp folder.
I am using following code also.
+ (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL {
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;
}
calling this method here:-
+ (void) createEditableCopyOfDatabaseIfNeeded
{
NSLog(#"Creating editable copy of database");
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:ddb];
[self addSkipBackupAttributeToItemAtURL:[NSURL URLWithString:writableDBPath]];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success)
{
NSLog(#"ALready exists"); return;
}
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:ddb];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
[self addSkipBackupAttributeToItemAtURL:[NSURL URLWithString:defaultDBPath]];
if (!success)
{
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
NSURL * fileURL;
fileURL = [ NSURL fileURLWithPath: ddb ];
[ fileURL setResourceValue: [ NSNumber numberWithBool: YES ] forKey: NSURLIsExcludedFromBackupKey error: nil ];
}
Still my application was rejected.Please help.
I got this respones from apple.
May 12, 2016 at 1:59 AM
From Apple
2.23 - Apps must follow the iOS Data Storage Guidelines or they will be rejected
Thank you for your resubmission. During our continued review, we found the following issue unresolved:
2.23 Details
On launch and content download, your app still stores 78.84 MB on the user's iCloud, which does not comply with the iOS Data Storage Guidelines.
Next Steps
Please verify that only the content that the user creates using your app, e.g., documents, new files, edits, etc. is backed up by iCloud as required by the iOS Data Storage Guidelines. Also, check that any temporary files used by your app are only stored in the /tmp directory; please remember to remove or delete the files stored in this location when it is determined they are no longer needed.
Data that can be recreated but must persist for proper functioning of your app - or because users 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 kCRUFLIsExcludedFromBackupKey attribute.
Resources
To check how much data your app is storing:
- Install and launch your app
- Go to Settings > iCloud > Storage > Manage Storage
- Select your device
- If necessary, tap "Show all apps"
- Check your app's storage
For additional information on preventing files from being backed up to iCloud and iTunes, see Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes.
If you have difficulty reproducing a reported issue, please try testing the workflow described in Technical Q&A QA1764: How to reproduce bugs reported against App Store submissions.
If you have code-level questions after utilizing the above resources, you may wish to consult with Apple Developer Technical Support. When the DTS engineer follows up with you, please be ready to provide:
- complete details of your rejection issue(s)
- screenshots
- steps to reproduce the issue(s)
- symbolicated crash logs - if your issue results in a crash log
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:ddb];
This kinda points out ddb is some random string.
NSURL * fileURL;
fileURL = [ NSURL fileURLWithPath: ddb ];
[fileURL setResourceValue: [ NSNumber numberWithBool: YES ] forKey: NSURLIsExcludedFromBackupKey error: nil ];
here you are not getting the path of where you copied the db, instead
just opening url with string value which is why i think it is failing.
To fix this call the method after this line
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
[self addSkipBackupAttributeToItemAtURL:[NSURL URLWithString:writableDBPath]];
(1) Make sure your large file is located in the Library folder.
(2) Use the NSURLIsExcludedFromBackupKey option or the addSkipBackupAttributeToItemAtPath in AppDelegate.m .
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/* create a file outside of the Document folder like filePath1 */
/* preven os from copying data file for iCloud */
[self addSkipBackupAttributeToItemAtPath:[self filePathA]];
return YES;
}
- (NSString *)filePathA {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES);
return [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Data"];
}
- (NSString *)filePath1 {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES);
return [[[paths objectAtIndex:0] stringByAppendingPathComponent:#"Data"] stringByAppendingPathComponent:#"Data1.data"];
}
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)path {
NSURL *url = [NSURL fileURLWithPath: path];
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;
}

App Crashes at startup on App Store review - works perfectly on devices

I have an app that crashes on startup in the app store review, but works perfectly otherwise on devices and on the simulators. Here are the specifics.
The app is written for IOS 7, and is being tested on IOS 8.x. I symbolicated the crash logs from Apple, and it is crashing on the first attempt to access information stored in the pre-populated Core Data sqlite db.
This code does the db copy at startup:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"CC.sqlite"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *storePath = [documentsDirectory stringByAppendingPathComponent: #"CC.sqlite"];
// Check if the sqlite store exists
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath]) {
NSLog(#"sqlite db not found... copy into place");
// copy the sqlite files to the store location.
NSString *bundleStore = [[NSBundle mainBundle] pathForResource:#"CC" ofType:#"sqlite"];
[[NSFileManager defaultManager] copyItemAtPath:bundleStore toPath:storePath error:nil];
}
else {
NSLog(#"Already exists");
}
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = #{ NSSQLitePragmasOption : #{#"journal_mode" : #"DELETE"} };
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(#"error %#, %#", error, [error userInfo]);
}
return __persistentStoreCoordinator;
}
We have run this a couple of hundred times on various IOS devices (by several different people) without any problems at all, but invariably get a startup crash at Apple.
It is clear to me from the crash logs that the app (during app store review) is trying to access a non-existant sqlite db through Core Data, but I have no idea why this is happening only at Apple, and why I cannot reproduce the error. I am not sure what other info to add to the question but will happily update as required.
Any advice gratefully received....
I will answer my own question here, although the code changes I made while trying to solve the problem makes posting the code not very helpful.
I had to put the startup database operations in a completion block. This involved moving everything into my initial startup view controller, and disabling my tab bar buttons while the database was initialized.
What was happening was certain items in the Core Data sqlite database were being required before it was completely preloaded, thus the crash.
More on blocks can be found here

ios: Where to put the Sqlite file in solution?

I am new to iOS development, I am making changes in an application which is using Sqlite. I am to add some new fields in some tables, I browsed DB with software and added new fields in
inventory_db_src.sqlite but when I see in emulator it uses inventory_db.sqlite which is strange as there is no inventory_db.sqlite file in solution and neither code creating DB through SQL script. And If I debug code it gets inventory_db.sqlite path successfully and never executes inventory_db_src.sqlite line and put inventory_db.sqlite in emulator where my new fields are not present as I put these in inventory_db_src.sqlite. pLease help me
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dbPath = [documentsDirectory stringByAppendingPathComponent:#"inventory_db.sqlite"];
// [fileManager removeItemAtPath:dbPath error:nil];
success = [fileManager fileExistsAtPath:dbPath];
success = NO;
if (success) {
int savedVersion = [[NSUserDefaults standardUserDefaults] integerForKey:kVERSION_KEY];
if (kCURRENT_DB_VERSION != savedVersion) {
[fileManager removeItemAtPath:dbPath error:nil];
success = NO;
}
}
if (!success) {
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"inventory_db_src.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
_INVENTORY_DB = [[FMDatabase alloc] initWithPath:dbPath];
Line 6 in your code above is showing the name as inventory_db.sqlite, so that is the file your app will be using. You need to modify that to use the new database name. Be aware, that by modifying your database, you might have unexpected results so you will need to manage what DB version your app is using and make data corrections as needed. This is one nice feature that Core Data can assist in.
According to your statement it seems that old DB is cached in to your Emulator. please reset you Emulator by
iOS Simulator -> Reset content and settings
And it should work ...

newsstand memory storage issue, how do i get the app cache directory?

I have a newsstand app which has magazines and uses the newsstand framework. I realized there was something wrong when deleting the magazines and/or when downloading them because when I accessed settings/usage my app keeps growing in memory usage when downloading and deleting the same magazine.
Found the issue... when downloading the issue in the delegate method:
-(void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL
I just needed to add something like this at the end:
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:[destinationURL path] error:&error];
if (error){
NSLog(#"ERROR:%#", error);
}
Even the directory is called "caches" you need to manually delete. Ok problem solved but what about the customers who already download my app and have tons of MBs dead in the cache directory.
I wanted to know how to get this directory and delete everything on it at launch and only once...
I can do it only once using a NSUserdefault but how do I get this directory and delete any zip files in it... an example of this directory and a file within is:
/private/var/mobile/Applications/1291CC20-C55F-48F6-86B6-B0909F887C58/Library/Caches/bgdl-280-6e4e063c922d1f58.zip
but this path varies with the device. I want to do this at launch so I'm sure there are no downloads in progress but any other solutions are welcome, thanks in advance.
Everything that you need is enumerate all files from Caches directory and remove ones that have zip extension:
- (void)removeZipFilesFromCachesDirectory {
static NSString *const kZIPExtension = #"zip";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *cachesDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSError *error = nil;
NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:cachesDirectoryPath error:&error];
if (error == nil) {
for (NSString *fileName in fileNames) {
NSString *filePath = [cachesDirectoryPath stringByAppendingPathComponent:fileName];
if ([filePath.pathExtension.lowercaseString isEqualToString:kZIPExtension]) {
NSError *anError = nil;
[fileManager removeItemAtPath:filePath error:&anError];
if (anError != nil) {
NSLog(#"%#", anError);
}
}
}
} else {
NSLog(#"%#", error);
}
}

NSURLIsExcludedFromBackupKey - Apps must follow the iOS Data Storage Guidelines or they will be rejected

My app was rejected cause it seems that 7 mb are stored in documents folder and they are automatically send to icloud. So i have looped all files that will be written to documents folder throught this method :
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL {
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
if (&NSURLIsExcludedFromBackupKey == nil) {
// iOS 5.0.1 and lower
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}
else {
// First try and remove the extended attribute if it is present
int result = getxattr(filePath, attrName, NULL, sizeof(u_int8_t), 0, 0);
if (result != -1) {
// The attribute exists, we need to remove it
int removeResult = removexattr(filePath, attrName, 0);
if (removeResult == 0) {
NSLog(#"Removed extended attribute on file %#", URL);
}
}
// Set the new key
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
The version 1.1 of my app was approved after this code implementation. Last week i tried to send the version 1.2 of the same app (nothing has changed in file management, all files that are stored in documents folder are looped through the addSkipBackupAttributeToItemAtURL method). My app was rejected again for the same reason.  I can't move my file to temp or cache folder because my app can't completely restore the file (one of this file is a db, restoring db means loose any user inserted data), so this one can't be the solution. Anyway i have found an issue in the code, this is how i call the method :
[self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:fullPath]];
using [NSURL fileURLWithPath:fullPath] device with ios 5.1 return an error and it seems impossible to create the attribute. If i change the initialization of the nsurl with [NSURL URLWithString:defaultStorePath], device with 5.1 seems to add correctly the attribute.
With ios 5.0.1 all is inverted , [NSURL URLWithString:defaultStorePath] return an error while [NSURL fileURLWithPath:fullPath] works.
Maybe i can check ios version and set an appropriate nsurl initialization, but  it  still remains a problem. In rejection explanation i read :
In particular, we found that on launch and/or content download, your app stores 7mb. 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
If i try to check this value i see 7 mb also with the correct nsurl initialization (when all the attributes are set  correctly) . What is the correct behaviour? Anyone with this problem?  Do I have to do something specific before the app storage check suggested by apple to make it significant?
I think the trick is to add the NSURLIsExcludedFromBackupKey AND make sure the directory is outside the documents directory. I did this by moving my documents to the Library/Application Support folder (since it didn't make sense in the /tmp or /Caches folders):
// store in /Library/Application Support/BUNDLE_IDENTIFIER/Reference
// make sure Application Support folder exists
NSURL *applicationSupportDirectory = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:&error];
if (error) {
NSLog(#"KCDM: Could not create application support directory. %#", error);
return nil;
}
NSURL *referenceFolder = [applicationSupportDirectory URLByAppendingPathComponent:#"Reference" isDirectory:YES];
if (![[NSFileManager defaultManager] createDirectoryAtPath:[referenceFolder path]
withIntermediateDirectories:YES
attributes:nil
error:&error]) {
NSLog(#"KCDM: Error creating Reference folder to store model %#: %#", modelName, error);
return nil;
}
BOOL success = [referenceFolder setResourceValue:#YES forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(#"KCDM: Error excluding %# from backup %#", referenceFolder, error);
}
I had the same problem as you until I deleted my app from my device, and re-installed. I also had to delete the existing cached data from the iCloud backup by going to Settings->Storage&Backup -> Manage Storage
That seemed to do the trick.
Also, my code to add the skip attribute is a bit different:
Code lifted from this post
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
if (&NSURLIsExcludedFromBackupKey == nil) { // iOS <= 5.0.1
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;
}
else { // iOS >= 5.1
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
}

Resources