How to know if NSURLIsExcludedFromBackupKey is working? - ios

I recently had my app rejected due to:
2.23: Apps must follow the iOS Data Storage Guidelines or they will be rejected
The reason for this is my app downloads a lot of image files from the web and writes them to the documents directory. So I do the following to try and remedy the situation:
NSURL *url = [NSURL URLWithString:stickerURL];
NSError *error = nil;
BOOL success = [url setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
if(!success)
{
NSLog(#"Error excluding %# from backup %#", [url lastPathComponent], error);
}
NSData *data = [NSData dataWithContentsOfURL:url];
My question is, how am I to know if NSURLIsExcludedFromBackupKey is working? The BOOL success is always coming back as YES. However, when I check Settings -> iCloud -> Storage & Backup -> Manage Storage -> MyiPhone5, it's making no difference in the size that is being shown underneath my app. Would this mean it's not working? If not, what can I fix in my code?

If you are checking "Manage Storage" in your device's settings, and after adding a bunch of files (eg., photos, documents, or whatever it is you're flagging to not be backed up) you notice that the backup size for you app is increasing, then the flag isn't "working" (although that shouldn't be the case, really).
One way to test it is to get rid of the key NSURLIsExcludedFromBackupKey, download a bunch of data, and then check to see if the backup size is increasing. Then, once you add the key back into your code, and once again download a bunch of data, the backup storage size should really not increase noticeably.

Related

Flag "do-not'backup" files in iOS Main Bundle

I uploaded an app to Appstore but got rejected due to files being backed up to iCloud, when only user generated content should be backed up.
The thing is that I have a json in MainBundle that I use only the first time to create and update CoreData. Once I update CoreData, the json is no longer needed. I tried a solution to delete this json file but I can't since it's not permitted by Apple to delete or edit content from the Main Bundle. I, then, leave the json unused and in the Main Bundle, but the app was rejected due to this file being backed up to iCloud.
I tried this solution below, that Apple gave me to solve this problem:
- (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;
}
I still can't solve the problem, because every time I try to use this code, I get this error:
NSUnderlyingError=0x15f87bb0 "The operation couldn’t be completed. Operation not permitted"
My understanding is that I can't flag the json as "do-not-backup" because I can't delete or edit any files that are in MainBundle. I tried arguing with Apple that this happens but got the same response from them.
Any ideas on how to solve this problem?

App does not store any content in document directory but Appstore reject

I am new to iOS development. My app got rejected from the review, stating the following reason,
2.23 Apps must follow the iOS Data Storage Guidelines or they will be rejected
We found that your app does not follow the iOS Data Storage Guidelines, which is required per the App Store Review Guidelines.
I am not storing my DB file in documents directory. Here's my code,
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [libraryPath stringByAppendingPathComponent:#"DatabaseFolder"];
NSURL *pathURL = [NSURL fileURLWithPath:path];
BOOL isDirectory = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]) {
if (isDirectory) {
return pathURL;
} else {
// Handle error. ".data" is a file which should not be there...
[NSException raise:#"'Private Documents' exists, and is a file" format:#"Path: %#", path];
}
}
NSError *error = nil;
if (![[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]) {
[NSException raise:#"Failed creating directory" format:#"[%#], %#", path, error];
}
return pathURL;
How to reproduce a crash or bug that only App Review or users are seeing?
The iOS Data Storage guideline document (login required to view) says,
Everything in your app’s home directory is backed up, with the exception of the application bundle itself, the caches directory, and temp directory.
This means even your NSLibraryDirectory directory contents gets backed up to iCloud. To resolve this you have following options,
Use the /tmp directory for storage
Use the /Caches directory for storage
Only use the /Documents directory for user-generated content that cannot be re-created.
Set the do not backup attribute on the file using setResourceValue:forKey:error: method of NSURL.
Here is how you can mark a resource for not backing up to iCloud.
- (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;
}
Hope that helps!
I assume the reviewer doesn't like that you are storing the database in the library folder and there within one created by you. If you read the mentioned guidelines you'll see that you shouldn't store there.
Data that can be downloaded again or regenerated should be stored in the /Library/Caches directory. Examples of files you should put in the Caches directory include database cache files and downloadable content, such as that used by magazine, newspaper, and map applications
I had this problem for a while also. So I made a class to handle this for me. There are different rules of where you can store stuff in different OS's. So my class checked the OS and returned a proper data director for each one and even handled the migration of data from one location to the other if the OS was updated.
But pretty much today you could just support the 5.1 and up location and be fine.
The key is that you need to set your do not backup attribute also.
I just put in a github here: https://github.com/badweasel/BWFileManager

iCloud: Sync Core Data between IOS and OSX

I try to sync core data between IOS and OSX. At both apps I have the same configuration:
And the same entitlements:
I also use the same code for the store coordinator within the same name for sqlite file and url:
NSManagedObjectModel* managedModel = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator* storeCooordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedModel];
//-> start iCloud
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL* applicationFilesDirectory = [JHDCoreDataDAO applicationFilesDirectory];
NSURL* storeURL = [applicationFilesDirectory URLByAppendingPathComponent:DATABASE_NAME];
if(!storeURL) { NSLog(#"Error reading applicationFilesDirectory for given sqlite resouce"); }
NSString* containerIdentifier = [NSString stringWithFormat:#"%#.%#",TEAM_IDENTIFIER,APP_IDENTIFIER];
NSURL* iCloud = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:containerIdentifier];
NSString *iCloudEnabledAppID = #"TEAMID~com~sample~sample";
NSError* persistentStoreError;
if (iCloud) {
NSLog(#"iCloud is working");
NSLog(#"iCloudEnabledAppID = %#",iCloudEnabledAppID);
NSLog(#"iCloud URL: %#",iCloud);
NSString* cloudPath = [[iCloud path] stringByAppendingPathComponent:#"data"];
NSURL* transactionsLogUrl = [NSURL fileURLWithPath:cloudPath];
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:
iCloudEnabledAppID, NSPersistentStoreUbiquitousContentNameKey,
transactionsLogUrl, NSPersistentStoreUbiquitousContentURLKey,
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
nil];
[storeCooordinator lock];
if(![storeCooordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&persistentStoreError])
{
NSLog(#"Fehler: %#, %#", persistentStoreError.localizedDescription, persistentStoreError.userInfo);
abort();
}
[storeCooordinator unlock];
} else {
//Handle local psc
}
});
Each app, the IOS Version and the OSX Version, are still running perfectly within the iCloud. Each app handle its own database, due to there is no sync between this two apps. Is there somethings what I have forgotton?
You need to be very careful that the identifiers you use in the entitlements match what you use in the source code to access the ubiquity container.
One complication when syncing multiple apps (e.g. Mac app and iOS app) is that you will need to check that they each have the same team identifier. If not, you will want to fill in the team id explicitly for iCloud entitlements, rather than using the TeamIdentifierPrefix variable. Pick one of the team ids and use that for both apps.
In your particular example, it looks like you have entitlements setup for a container id that ends in sample.sample.sample. Assuming the team id is the same for each app, your container id should be XXXXXXXXXX.sample.sample.sample, where the first part is your team id. I don't know what APP_IDENTIFIER is in this instance, but it should be checked to make sure it is sample.sample.sample. (My guess is that it isn't.)
The NSPersistentStoreUbiquitousContentNameKey setting can be basically any label you like for your store. It doesn't have to use the ~ or even be reverse-DNS form. For example, you could call it 'Store1'.
I find the easiest way to see if everything is setup OK is to go to the ~/Library/Mobile Documents folder on your Mac, and look for the app container. You can just browse the contents directly in Finder.
Unfortunately, this is probably the easiest part of getting Core Data working with iCloud. There will be many more hurdles, and they tend to be more challenging. There are new solutions coming up for syncing Core Data all the time, including the TICDS framework and Core Data Ensembles, both of which work with iCloud. (Disclosure: I have contributed to both projects, and founded the Ensembles project.)

Can I mark Documents directory with "do not backup" attribute?

I had read that I can mark folders with "do not backup" attribute.
As I understand, in such case all contents of directory will be excluded from backups.
In our app we need to exclude from backup all files in Documents directory (the files can be added or deleted from Documents during app execution).
We need to store our files in Documents because we use "Application supports iTunes file sharing" feature.
Can we mark Documents directory with "do not backup attribute"?
Does Apple permits this?
Could this become the reason to reject our app?
According to apple
In iOS 5.0 and earlier, put files in the
/Library/Caches directory to prevent them from being
backed up
In iOS 5.0.1 and later, put files in the
/Library/Application Support directory and apply the
com.apple.MobileBackup extended attribute to them. This attribute prevents the files from being backed up to iTunes or iCloud. If you
have a large number of support files, you may store them in a custom
subdirectory and apply the extended attribute to just the directory.
As far as I know
You can not mark documents directory with do not back up attribute
1)you may mark up the individual files inside the documents directory using below code snippet
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)filePathString {
NSURL *fileURL =
[NSURL fileURLWithPath:filePathString];
assert([[NSFileManager defaultManager]
fileExistsAtPath: [fileURL path]]);
NSError *error = nil;
BOOL success = [fileURL setResourceValue:[NSNumber numberWithBool:YES]
forKey:NSURLIsExcludedFromBackupKey
error:&error];
return success;
}
2)You may create a subdirectory inside documents folder and apply extended attribute to that.
you may set extended attribute using the below syntax.
int result = setxattr(path, attrName, myDataBytes, [myData length], 0, 0);
you can find more information on reading and writing extended attributes in the following link
I hope this helps

How to use NSURLIsExcludedFromBackupKey Or kCFURLIsExcludedFromBackupKey?

My App had been rejected because I save in-app purchase data in Documents folder on iPhone.
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.
But I want the user to use the data even if they are offline, so I'll use kCFURLIsExcludedFromBackupKey or NSURLIsExcludedFromBackupKey. What is the different between them?
The question is how to use any of them, and what will it return and how can I use this returned data?
NSError *error = nil;
BOOL result = [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];

Resources