Saturday, April 9, 2016

Large files downloaded to Documents and backed up to iCloud

Leave a Comment

I have an iOS app in the app store that can download relatively large files that need to stay on the device for offline use. Those files are currently stored in the app's Documents folder but I'm just now reading that the Documents folder is backed up and should really only be used for user-generated content. This Apple technical Q&A states that the NSURLIsExcludedFromBackupKey should be set to prevent backup. This states that an app's /Library/Caches is the right place to put these kinds of files although further reading suggests that the folder may be cleared when the device is low on storage which is unacceptable for this app. I believe /Library/Application Support/ is then the best location for them -- does this sound right?

Unfortunately, this mistake got through the app review process. What are some best practices for fixing this now that people are using the app and already have some files persisted to the Documents folder and to their backups? It seems I need to move all the existing files and set their NSURLIsExcludedFromBackupKey on app update. How do I guarantee that this is done exactly once and that it isn't interrupted? Is moving the files out of the Documents folder important or could I leave them there? Will changing the files' backup status remove them from existing backups?

I'm using Swift 2.1.1 and targeting iOS 8.0+.

2 Answers

Answers 1

As stated in the technical Q&A, you best bet could be create a subdirectory in the Documents, and exclude that subdirectory once.

Answers 2

I don't believe you can write a 'do it once and be sure it is done' routine, since you can't guarantee your app doesn't crash while it is running. You certainly could set a completion flag when you are sure it is done so that once it is done you don't have to run it again.

Exclude your directory from backup, not the individual files. From Xcode:

You can use this property to exclude cache and other app support files which are not needed in a backup. Some operations commonly made to user documents cause this property to be reset to false; consequently, do not use this property on user documents.

Here is the strategy I have used with good results (sorry, its in objective-c -- I'm not a swift guy. Hopefully it will give you the idea):

    - (BOOL)moveAllFiles{      // Searches through the documents directory for all files ending in .data     BOOL success = true;     NSString *myNewLocation = @"/store/my/files/here/now";      // Get the documents directory     NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);     NSString *documentDirectory = [documentDirectories objectAtIndex:0];      // Get all files ending in .data (whatever your file type is)     NSArray *dataFilesArray = [NSArray arrayWithArray:[NSBundle pathsForResourcesOfType:@"data" inDirectory:documentDirectory]];     // If you have multiple resource types, use this line once each for each resource type, then append the arrays.      // Iterate the found files     NSString *fileName = [NSString string];     for (int i=0; i<[dataFilesArray count]; i++) {         fileName = [[dataFilesArray objectAtIndex:i] lastPathComponent];          // Move file, set success to false if unsuccessful move         if (![[NSFileManager defaultManager] moveItemAtPath:[dataFilesArray objectAtIndex:i]                                                     toPath:[myNewLocation stringByAppendingPathComponent:fileName]                                                      error:nil]) {             success = false; // Something went wrong         }     }     return success; } 

Now use the value of success to set a key in the user defaults file. Check for that key on startup. If it is false (or absent), run this routine (again).

This example is with file paths. You can do the same thing with file URLs if you wish.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment