Monday, November 21, 2011

iCloud – file backup with no UIDocument whistles. Part I.

I’m on a quest to add a backup feature to one of the iOS apps of mine (blocnote). App is only provided for iOS 5+ and my first option to use would be iCloud over Dropbox et al. I don’t want to use a UIDocument as well, as here it is only about simple file backup, no intent to keep this file in sync.

After reading what I could (as Apple docs on iCloud can’t really be taken as a good reference):

http://www.raywenderlich.com/6015/beginning-icloud-in-ios-5-tutorial-part-1
http://www.raywenderlich.com/6031/beginning-icloud-in-ios-5-tutorial-part-2
http://stackoverflow.com/questions/7795629/icloud-basics-and-code-sample

First stop would be to satisfy a requirement to create a single backup file from a sqlite db and put into the iCloud.

Providing some very raw bits of code here, only building proof of concept at this stage!

Checking for a iCloud availability:

+ (bool) isiCloudAvailable
{
    NSURL *ubiq = [[NSFileManager defaultManager] 
                   URLForUbiquityContainerIdentifier:nil];
    if (ubiq) {
        NSLog(@"iCloud access at %@", ubiq);
        return true;
    } else {
        NSLog(@"No iCloud access");
        return false;
    }
}

Getting db file copied inside the same documents directory:

NSString *bName = [NSString stringWithFormat:@"%@_%@%@",backupName,[Formatter dateToBackupNameString:[NSDate date]],@".db"];
    
    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentFolderPath = [searchPaths objectAtIndex: 0];
    
    NSString *backupPath = [documentFolderPath stringByAppendingPathComponent:bName];
    NSURL *backupUrl = [NSURL URLWithString:backupPath];
    
    NSError *copyError = nil;
    NSError *removeBckError = nil;
 
    if ([[NSFileManager defaultManager] fileExistsAtPath:backupPath]) {
        bool success = [[NSFileManager defaultManager] removeItemAtPath: backupPath error:&removeBckError];
        if (!success) {
            NSLog(@"Bck cleanup error: %@", [removeBckError localizedDescription]);
        }
    }
    BOOL copiedBackupDb = [[NSFileManager defaultManager] copyItemAtPath:dbFilePath toPath:backupPath error:&copyError];
    if (!copiedBackupDb)
    {
        NSLog(@"bck db copy error: %@", [copyError localizedDescription]);
        return NO;
    }

And moving a copy to iCloud:

    NSFileManager*  fm = [NSFileManager defaultManager];
    
    NSURL *ubiq = [[NSFileManager defaultManager] 
                   URLForUbiquityContainerIdentifier:nil];
    
    if (ubiq == nil) {
        return NO;
    }
    
    NSError *theError = nil;
    
    
    [fm setUbiquitous:true itemAtURL:backupUrl destinationURL:[[ubiq URLByAppendingPathComponent:@"Documents" isDirectory:true] URLByAppendingPathComponent:bName] error:&theError];
    
    if (theError != nil) {
        NSLog(@"iCloud error: %@", [theError localizedDescription]);
    }

Note that apple docs say you should not be using setUbiquitous on the main thread to avoid deadlocks. Plus there is no indication of progress is provided by this code yet to user. That will come…

Attention should be paid where exactly you move your file in iCloud. Without that destinationURL:[[ubiq URLByAppendingPathComponent:@"Documents" isDirectory:true] URLByAppendingPathComponent:bName] your file will end up in the common app documents&data container and user will have no ability to delete a separate backup file:

image

When put inside Documents in iCloud, files can be seen like:

image

And can be deleted by user out of iCloud nicely one by one.

This is it for today’s part, going for more exploration and will share as it goes.

Yours, Stan.

Post a Comment