I'm aware that iOS has API's to integrate iCloud for the apps. Can I integrate iCloud in mac apps as well? Will the implementation be different for Mac apps to integrate iCloud? If Yes, are there any tutorials etc or reference websites?
Yes. iCloud is available on the Mac.
But Apple's documentation for this topic still isn't very complete. The only official resources I could find where the WWDC 2011 session 107 video and some notes in "What's new in Mac OS X"
When Lion & iCloud still were under NDA I posted my findings in Apple's devforums.
This is is an edited version of this post:
I am using a modified version of the WWDC 2011 Session 107 code. (transcribed from the video) I had to remove the manual instantiation of NSFileCoordinator to get the sample working (the speaker mentions that the coordinator "might not be needed in the future"):
- (IBAction)moveToOrFromCloud:(id)sender
{
NSFileManager* fm = [NSFileManager defaultManager];
NSURL* fileURL = [[self document] fileURL];
BOOL shouldMakeUbiquitous = [sender tag] == 1;
NSURL* destinationURL;
if(shouldMakeUbiquitous)
{
NSURL* rootURL = [fm URLForUbiquityContainerIdentifier:@"app.example"];
NSURL* directoryURL = [rootURL URLByAppendingPathComponent:@"Documents"];
[fm createDirectoryAtURL:directoryURL withIntermediateDirectories:NO attributes:nil error:NULL];
destinationURL = [directoryURL URLByAppendingPathComponent:[fileURL lastPathComponent]];
}
else
{
destinationURL = [[[fm URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0] URLByAppendingPathComponent:[fileURL lastPathComponent]];
}
NSError* error;
if(![fm setUbiquitous:shouldMakeUbiquitous itemAtURL:fileURL destinationURL:destinationURL error:&error])
{
[[self document] presentError:error modalForWindow:[[self document] windowForSheet] delegate:nil didPresentSelector:NULL contextInfo:NULL];
}
else
{
[[self document] setFileURL:destinationURL];
[[self document] setFileModificationDate:nil];
}
}
The above IBAction is connected to a NSMenuItem that checks if the document is already in iCloud or if it needs to be uploaded:
- (BOOL)validateMenuItem:(NSMenuItem*)item
{
SEL action = [item action];
if (action == @selector(moveToOrFromCloud:))
{
BOOL isUbiquitous = [[NSFileManager defaultManager] isUbiquitousItemAtURL:[[self document] fileURL]];
[item setTitle:isUbiquitous ? @"Remove from Cloud": "Move to Cloud"];
[item setTag:isUbiquitous?0:1];
return [self.document fileURL] != nil;
}
return YES;
}
Checklist for non-code tasks that are necessary to get iCloud document storage working:
There is a document by Apple that is prety detailed in all aspects http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/iCloud/iCloud.html
One significant issue with the code posted here is that it does not have the team in the identifier given to URLForUbiquityContainerIdentifier even if it is mentioned in the checklist, leaving it nil altogether to be automatically filled from the entitlements seems like the best aproach.
Personally the only changes i had to do to get iCloud going in my app were :
That was all, here is a hopefully clearer sample code (should work for both iOS and OSX) :
NSURL *url = [self getiCloudURLFor:@"foo.bar" containerID:nil]; //leaving nil so it is auto filled from entitlements
if (url) {
NSError *error;
if (![[NSFileManager defaultManager] startDownloadingUbiquitousItemAtURL:url error:&error]) {
NSLog(@"Error downloading/syncing %@ (%@)",[url path],[error description]);
}else{
NSLog(@"Started downloading/syncing %@",[url path]);
}
}
NSArray *conflicts = [NSFileVersion unresolvedConflictVersionsOfItemAtURL:url];
for (NSFileVersion *conflict in conflicts) {
NSLog(@"Conflicting %@ at %@ by %@ from %@",[url path],[conflict URL],[conflict localizedNameOfSavingComputer],[conflict modificationDate]);
}
- (NSURL*)getiCloudURLFor:(NSString*)fileName containerID:(NSString*)containerID
{
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *rootURL = [fm URLForUbiquityContainerIdentifier:containerID];
if (rootURL) {
NSURL *directoryURL = [rootURL URLByAppendingPathComponent:@"Documents"];
if (![fm fileExistsAtPath:[directoryURL path]]) [fm createDirectoryAtURL:directoryURL withIntermediateDirectories:NO attributes:nil error:NULL];
NSURL *cloudURL = [directoryURL URLByAppendingPathComponent:fileName];
if (![fm isUbiquitousItemAtURL:cloudURL]) [self makeUbiquitousItemAtURL:cloudURL];//this only runs once per filename when it is first added to iCloud
return cloudURL;
}else{
return [[[fm URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0] URLByAppendingPathComponent:fileName]; //no cloud
}
return nil;
}
- (void)makeUbiquitousItemAtURL:(NSURL*)cloudURL
{
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *localURL = [[[fm URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0] URLByAppendingPathComponent:[cloudURL lastPathComponent]];
if (![fm fileExistsAtPath:[localURL path]]) [fm createFileAtPath:[localURL path] contents:nil attributes:nil];
NSError *error;
if(![fm setUbiquitous:YES itemAtURL:localURL destinationURL:cloudURL error:&error]) {
NSLog(@"Error making %@ ubiquituous at %@ (%@)",[localURL path],[cloudURL path],[error description]);
}else{
NSLog(@"Made %@ ubiquituous at %@",[localURL lastPathComponent],[cloudURL path]);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With