I'm seeking advice on using iCloud in a limited way. The docs are focused on syncing between multiple devices, which I do not need. When I say iCloud, I'm also including iCloud Drive, if that matters.
My iPhone app stores its data and state in a single SQLite database file that I want to save to iCloud (or iCloud Drive) for the purpose of backup, not for syncing changes between multiple devices. At launch, SQLite opens the database file and uses it synchronously (I'm using FMDB). The database file should remain on the device so, if iCloud is down, SQLite won't know or care. iCloud would have a shadow copy.
The iCloud copy does not always need to be current -- I'm okay with programmatically initiating an update to iCloud, and the restore if necessary. If the local database file becomes corrupted or if the user deletes the app, I would restore the file from iCloud. The file would usually be smaller than 1 MB, but might be large (100 MB), but mostly invariant.
I'm not syncing because of the data integrity risks of sharing whole SQLite files, and I cannot convert to Core Data (it's a huge app using FMDB).
I know that the iOS does daily backups to iCloud, and that would be sufficient if I could restore just my app from iCloud (and not the entire device).
I need advice on using iCloud for a shadow backup and restore, but not syncing.
UPDATE: I have made progress on this issue; I can save and restore my real SQLite file to/from iCloud. I subclassed UIDocument
with these two overrides:
@implementation MyDocument
// Override this method to return the document data to be saved.
- (id)contentsForType:(NSString *)typeName error:(NSError * _Nullable *)outError
{
NSString *databasePath = ... // path to my SQLite file in Documents;
NSData *dbData = [NSData dataWithContentsOfFile: databasePath];
return dbData; // the entire database file
}
// Override this method to load the document data into the app’s data model.
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError * _Nullable *)outError
{
NSString *databasePath = ... // path to my SQLite file in Documents;
NSData *dbData = contents;
BOOL ret = [dbData writeToFile: databasePath atomically:YES];
return YES;
}
@end
My SQLite database file is in Documents and it stays there, but I write the shadow copy to iCloud thusly:
- (void)sendToICloud
{
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSString *iCloudFileName = @"..."; // a file name I choose
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:@"Documents"]
URLByAppendingPathComponent:iCloudFileName];
MyDocument *myDoc = [[MyDocument alloc] initWithFileURL:ubiquitousPackage];
[myDoc saveToURL:[myDoc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if ( success ) {
[myDoc closeWithCompletionHandler:^(BOOL success) {
}];
} else {
NSLog(@"iCloud write Syncing FAILED with iCloud");
}
}];
}
Now my issues are:
I have created a custom container in Xcode that appears in my entitlements file and in the Dev portal for the App ID. I can save documents to iCloud from either of my two devices (iPhone and iPad). But, my two devices do not see each other's files. There are numerous old threads about this with random solutions, but nothing has solved the problem.
The iCloud documentation says files are updated incrementally, but I don't know if that applies in my case. If I use a new filename for
MyDocument
(myiCloudFileName
, above), I realize that an entire new file will be sent to iCloud. But, if I reuse the previous filename and send an updated NSData each time (as I'm doing inloadFromContents:ofType:error:
), will iOS only send the parts that have changed since the last time I saved the file?