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

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?

Related

How to add files to an App Group shared directory?

So I have an app with a share extension each with their own target. The core of the apps' bundle identifier is com.company.app and the share extensions is com.company.app.share. Each have their own entitlements file, with the core apps being App Groups -> group.com.company.app and the share extension being the same. Both of the bundle identifiers are listed on the apple developer website but for some reason,
I don't seem to have the right permissions to write to the shared private/var/mobile/Containers/Shared/AppGroup/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Library/Caches/ directory.
The error message I get is:
You don’t have permission to save the file “Caches” in the folder “Library”.
The directory is being saved like this:
GetZipURLInItems(self.extensionContext.inputItems, ^(NSURL *url, NSError *error) {
if (error == nil) {
NSURL *containerURL = [[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:GroupIdentifier] URLByAppendingPathComponent:#"Library/Caches"];
NSLog(#"EXT: containerURL: %#", containerURL);
[WPZipArchive unzipFileAtPath:[url path] toDestination:[containerURL absoluteString]];
[self listDirectoryAtPath:[containerURL absoluteString]];
}});
What am I'm missing here?
The issue was actually using [url absoluteString] vs [url path], changing all the urls to path solved the permissions error.

iOS - Can't Read File Located In Documents Directory

I am trying to read a file in which I successfully downloaded and saved in the Documents directory. However, when I try to read it, if fails.
Here is the error:
2016-03-28 21:00:26.585 App[569:4103] Path is /var/mobile/Applications/3AFA2430-C0DC-44CD-95F8-A89D82B2C348/Documents/combo.bin
2016-03-28 21:00:26.603 App[569:4103] Error in reading Error Domain=NSCocoaErrorDomain Code=257 "The operation couldn’t be completed. (Cocoa error 257.)" UserInfo=0x17df29d0 {NSFilePath=/var/mobile/Applications/3AFA2430-C0DC-44CD-95F8-A89D82B2C348/Documents/combo.bin, NSUnderlyingError=0x17df90b0 "The operation couldn’t be completed. Permission denied"}
Here is my code:
NSError *error;
NSData *firmwareContentData = [NSData dataWithContentsOfFile:FIRMWARE_LOCAL_PATH options:NSDataReadingMappedIfSafe error:&error];
NSLog(#"Path is %#", FIRMWARE_LOCAL_PATH);
if(error)
{
NSLog(#"Error in reading %#", error);
return;
}
local path is
#define FIRMWARE_LOCAL_PATH [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"combo.bin"]
I tried using NSTemporaryDirectory(), using NSFileManager to get the contents of the file, but none of them works. Any idea why? Thanks in advance!
You have a permissions error. You don't have the right permissions to open the file. Wherever you got it from, you're locked out. You might try to download it in the simulator, and check it through Apple's file system to see what permissions it actually downloaded with. The path is:
~/Library/Developer/CoreSimulator/Devices//data/Containers/Data/Application//Documents.
Replace the two big random strings with the directories that show a mod date of today, or NSLog the real path from your iOS app.

How to know if NSURLIsExcludedFromBackupKey is working?

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.

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

Black screen when attempting to start iCloud app on phone

I have this right now to trigger iCloud loading or not based on whether or not I'm in the simulator. When I try to run on a real device, I get a black screen and the 'addPersistentStore' line seems to hang. "My Project Name" is the name of the entitlements file, and the name of the app.
What's going on?
#if (TARGET_IPHONE_SIMULATOR)
if (![psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:dbUrl
options:nil
error:&error]) {
[NSException raise:#"Open failed" format:#"Reason: %#", [error localizedDescription]];
}
#else
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *ubContainer = [fm URLForUbiquityContainerIdentifier:nil];
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:#"My Project Name" forKey:NSPersistentStoreUbiquitousContentNameKey];
[options setObject:ubContainer forKey:NSPersistentStoreUbiquitousContentURLKey];
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:dbUrl options:options error:&error]) {
[NSException raise:#"Open failed" format:#"%#", [error localizedDescription]];
}
#endif
Apple recommends that when you're using iCloud, you should do all of these steps on a separate thread. Both URLForUbiquityContainerIdentifier and addPersistentStoreWithType:configuration:options:error: will connect to the network, and may block for long periods. The second call-- adding the persistent store-- can block for a lot longer. On iOS, iCloud data is only downloaded on demand, and this demand happens when you add the persistent store. You're getting a blank screen because NSPersistentStoreCoordinator is busy talking to the network (or trying to do so). Apple's sample code puts this on a separate queue, and you should do this too.
Your code doesn't indicate this, but you can't call -URLForUbiquityContainerIdentifier on the main thread. Note from the Apple documentation:
Important: Do not call this method from your app’s main thread.
Because this method might take a nontrivial amount of time to set up
iCloud and return the requested URL, you should always call it from a
secondary thread. To determine if iCloud is available, especially at
launch time, call the ubiquityIdentityToken method instead.
It could very well be that it takes a long time and that it appears as if your app isn't loading, while in reality it is just waiting for that method to return.

Resources