The Mental Blog

Software with Intellect

6 notes

Introducing ICACloud: iCloud Simplified

When you are working on a relatively big project like Ensembles, you sometimes develop classes which have utility in a much broader range of projects than just the one you happen to be working on. That was the case with CDEAsynchronousTaskQueue, which I recently posted about. CDEAsynchronousTaskQueue is a class that is useful for serializing a sequence of long-running asynchronous tasks, and maintaining close control over error handling policy.

There is another class in Ensembles which is also quite generic, and perhaps even more useful than CDEAsynchronousTaskQueue. It’s a class used to query iCloud, download files automatically, and transfer files in and out of the iCloud ubiquity container. Anyone who has ever had to do this knows it entails quite complex logic involving classes like NSFileCoordinator and NSMetadataQuery. If you want to go the extra mile, e.g. supporting timeouts for file coordination operations, it gets even uglier. In short, it makes iCloud complex and unfriendly to the inexperienced.

Wouldn’t it be good if you could just access iCloud like it was a web service, using a class that works like NSURLSession or the AFNetworking framework? iCloud is ultimately a web service with a local file cache, but interacting with that cache is complicated by file locking and other issues. Treating iCloud as a simple server makes things a lot simpler.

That’s what the ICACloud class is about. I have extracted the original class from Ensembles, renamed it, made it standalone, and pushed it to GitHub. It makes it much easier to work with iCloud, hiding details such as file coordination and metadata queries.

The methods of the class are fairly self explanatory, mirroring NSFileManager to some extent. One big difference is that most are asynchronous, with a completion callback block. The completion block includes an error parameter, which should be checked. If it is nil, the operation was successful, and if an NSError is supplied, it failed.

Cloud paths are relative to the ubiquity container, but you can optionally supply a path to a root directory in the container. This directory, and intermediate directories, will be created automatically if they don’t exist.

You should check the isConnected property to make sure the user is logged into iCloud before using the class.

Here is a simple example of using the ICACloud class.

ICACloud *cloud = [[ICACloud alloc] initWithUbiquityContainerIdentifier:@"XXXXXXXXXX.com.mycompany.cloudtest"
    rootDirectoryPath:@"Path/To/Data/Root"];
if (cloud.isConnected) {
    [cloud createDirectoryAtPath:@"Subdirectory" completion:^(NSError *error) {
        if (error) {
          NSLog(@"Failed to create subdirectory");
          return;
        }

        [cloud uploadLocalFile:@"/Users/me/Downloads/LocalImage.png" 
          toPath:@"Subdirectory/CloudImage.png" 
          completion:^(NSError *error) {
          if (error) NSLog(@"Failed to upload: %@", error);
        }];
    }];
}