Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Beginning iCloud Development - WhyMCA

Beginning iCloud Development - WhyMCA

How does iCloud work? How iCloud can enhance my applications? How should I design interaction with iCloud? When should I use iCloud? Which alternative services include similar features?

http://www.whymca.org/intervento/beginning-icloud-development

Cesare Rocchi

July 02, 2012
Tweet

More Decks by Cesare Rocchi

Other Decks in Programming

Transcript

  1. Outline What is iCloud? How does it work? Are there

    alternatives? Monday, July 2, 12
  2. iCloud Storyboards ARC OpenGL ES 2.0 News Stand Turn Based

    Gaming GameCenter API Monday, July 2, 12
  3. Cons (for devs) Stick to a synch model No http

    API No control on upload Monday, July 2, 12
  4. @implementation SMNote - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError { if

    ([contents length] > 0) { self.myContent = [[NSString alloc] initWithBytes:[contents bytes] length:[contents length] encoding:NSUTF8StringEncoding]; } else { // Default content self.myContent = @"Empty"; } return YES; } Monday, July 2, 12
  5. @implementation SMNote @synthesize noteContent; // Called whenever the application reads

    data - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError { } // Called whenever the application (auto)saves the content - (id)contentsForType:(NSString *)typeName error:(NSError **)outError { } Monday, July 2, 12
  6. Opening a document Build and run a query Wait for

    results Unfold results Monday, July 2, 12
  7. #import "SMNote.h" @interface SMAppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic)

    UIWindow *window; @property (strong, nonatomic) SMViewController *viewController; @property (strong) SMNote *doc; @property (strong) NSMetadataQuery *query; - (void)loadDocument; @end Monday, July 2, 12
  8. - (void)loadDocument { NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; _query

    = query; [query setSearchScopes:[NSArray arrayWithObject: NSMetadataQueryUbiquitousDocumentsScope]]; } Monday, July 2, 12
  9. - (void)loadDocument { NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; _query

    = query; [query setSearchScopes:[NSArray arrayWithObject: NSMetadataQueryUbiquitousDocumentsScope]]; NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, kFILENAME]; [query setPredicate:pred]; } Monday, July 2, 12
  10. - (void)loadDocument { NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; _query

    = query; [query setSearchScopes:[NSArray arrayWithObject: NSMetadataQueryUbiquitousDocumentsScope]]; NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, kFILENAME]; [query setPredicate:pred]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinish:) name:NSMetadataQueryDidFinishGatheringNotification object:query]; [query startQuery]; } Monday, July 2, 12
  11. - (void)queryDidFinish:(NSNotification *)notification { NSMetadataQuery *query = [notification object]; [query

    disableUpdates]; [query stopQuery]; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query]; _query = nil; ! [self loadData:query]; } Monday, July 2, 12
  12. - (void)loadData:(NSMetadataQuery *)query { if ([query resultCount] == 1) {

    NSMetadataItem *item = [query resultAtIndex:0]; NSURL *url = [item valueForAttribute:NSMetadataItemURLKey]; SMNote *doc = [[SMNote alloc] initWithFileURL:url]; } } Monday, July 2, 12
  13. - (void)loadData:(NSMetadataQuery *)query { if ([query resultCount] == 1) {

    NSMetadataItem *item = [query resultAtIndex:0]; NSURL *url = [item valueForAttribute:NSMetadataItemURLKey]; self.doc = [[SMNote alloc] initWithFileURL:url]; [self.doc openWithCompletionHandler:^(BOOL success) { if (success) { NSLog(@"iCloud document opened"); } else { NSLog(@"failed opening document from iCloud"); } }]; } } Monday, July 2, 12
  14. else { NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSURL *ubiquitousPackage

    = [[ubiq URLByAppendingPathComponent: @"Documents"] URLByAppendingPathComponent:kFILENAME]; SMNote *doc = [[SMNote alloc] initWithFileURL:ubiquitousPackage]; } Monday, July 2, 12
  15. else { NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; NSURL *ubiquitousPackage

    = [[ubiq URLByAppendingPathComponent: @"Documents"] URLByAppendingPathComponent:kFILENAME]; SMNote *doc = [[SMNote alloc] initWithFileURL:ubiquitousPackage]; self.doc = doc; [doc saveToURL: [doc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) { if (success) { [doc openWithCompletionHandler:^(BOOL success) { NSLog(@"new document opened from iCloud"); }]; } }]; } Monday, July 2, 12
  16. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [self.window makeKeyAndVisible]; NSURL

    *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; if (ubiq) { NSLog(@"iCloud access at %@", ubiq); [self loadDocument]; } else { NSLog(@"No iCloud access"); } return YES; } Monday, July 2, 12
  17. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [self.window makeKeyAndVisible]; NSURL

    *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; if (ubiq) { NSLog(@"iCloud access at %@", ubiq); [self loadDocument]; } else { NSLog(@"No iCloud access"); } return YES; } Monday, July 2, 12
  18. - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataReloaded:) name:@"noteModified"

    object:nil]; } - (void)dataReloaded:(NSNotification *)notification { self.doc = notification.object; self.noteView.text = self.doc.noteContent; } Monday, July 2, 12
  19. - (NSURL *) localNotesURL { return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]

    lastObject]; } - (NSURL *) ubiquitousNotesURL { return [[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] URLByAppendingPathComponent:@"Documents"]; } Monday, July 2, 12
  20. - (void) setNoteUbiquity { NSURL *baseUrl = [self localNotesURL]; if

    (_useiCloud) baseUrl = [self ubiquitousNotesURL]; NSURL *destUrl = [baseUrl URLByAppendingPathComponent: [note.fileURL lastPathComponent]]; [[NSFileManager defaultManager] setUbiquitous:_useiCloud itemAtURL:note.fileURL destinationURL:destUrl error:NULL]; } Don’t call it on the main thread! Monday, July 2, 12
  21. - (void) startMigration { NSOperationQueue *iCloudQueue = [NSOperationQueue new]; NSInvocationOperation

    *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(setNoteUbiquity) object:nil]; [iCloudQueue addOperation:op]; } Monday, July 2, 12
  22. @interface SMNote : NSObject <NSCoding> @property (copy, nonatomic) NSString *noteId;

    @property (copy, nonatomic) NSString *noteContent; @property (strong, nonatomic) NSDate *createdAt; @property (strong, nonatomic) NSDate *updatedAt; @end Monday, July 2, 12
  23. #import "SMNote.h" @interface SMNotesDocument : UIDocument @property (nonatomic, strong) NSMutableArray

    *entries; @property (nonatomic, strong) NSFileWrapper *fileWrapper; @end Monday, July 2, 12
  24. #import "SMNote.h" @interface SMNotesDocument : UIDocument @property (nonatomic, strong) NSMutableArray

    *entries; @property (nonatomic, strong) NSFileWrapper *fileWrapper; @end Monday, July 2, 12
  25. - (id)contentsForType:(NSString *)typeName error:(NSError **)outError { NSMutableDictionary *w = [NSMutableDictionary

    dictionary]; NSMutableData *data = [NSMutableData data]; NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [arch encodeObject:_entries forKey:@"entries"]; [arch finishEncoding]; } Monday, July 2, 12
  26. - (id)contentsForType:(NSString *)typeName error:(NSError **)outError { NSMutableDictionary *w = [NSMutableDictionary

    dictionary]; NSMutableData *data = [NSMutableData data]; NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [arch encodeObject:_entries forKey:@"entries"]; [arch finishEncoding]; NSFileWrapper *entriesWrapper = [[NSFileWrapper alloc] initRegularFileWithContents:data]; [w setObject:entriesWrapper forKey:@"notes.dat"]; // add other wrappers if you like NSFileWrapper *res = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:w]; return res; } Monday, July 2, 12
  27. - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError { NSFileWrapper *wrapper =

    (NSFileWrapper *)contents; NSDictionary *d = [wrapper fileWrappers]; NSFileWrapper *entriesWrap = [d objectForKey:@"notes.dat"]; } Monday, July 2, 12
  28. - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError { NSFileWrapper *wrapper =

    (NSFileWrapper *)contents; NSDictionary *d = [wrapper fileWrappers]; NSFileWrapper *entriesWrap = [d objectForKey:@"notes.dat"]; NSData *data = [entriesWrap regularFileContents]; NSKeyedUnarchiver *arch = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; _entries = [arch decodeObjectForKey:@"entries"]; // Notify the view } Monday, July 2, 12
  29. - (void) saveNoteAsCurrent { [[NSUbiquitousKeyValueStore defaultStore] setString:self.currentNote.noteId forKey:@"com.studiomagnolia.currentNote"]; [[NSUbiquitousKeyValueStore defaultStore]

    synchronize]; } NSString *currentNoteId = [[NSUbiquitousKeyValueStore defaultStore] stringForKey: @"com.studiomagnolia.currentNote"]; Monday, July 2, 12
  30. UIDocumentState s = [n documentState]; switch (s) { case UIDocumentStateNormal:

    NSLog(@"Everything is fine"); break; case UIDocumentStateInConflict: NSLog(@"There is a conflict"); break; ... default: NSLog(@"Unknown state"); break; } Monday, July 2, 12
  31. // iOS 5.0.1 #import <sys/xattr.h> - (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL { const

    char* filePath = [[URL path] fileSystemRepresentation]; const char* attrName = "com.apple.MobileBackup"; u_int8_t attrValue = 1; int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0); return result == 0; } Monday, July 2, 12
  32. // iOS 5.1 - (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL { NSError *error =

    nil; BOOL success = [URL setResourceValue: [NSNumber numberWithBool:YES] forKey: NSURLIsExcludedFromBackupKey error: &error]; if(!success){ NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error); } return success; } Monday, July 2, 12
  33. PFObject *note = [PFObject objectWithClassName:@"Note"]; [note setObject:@"Ciao" forKey:@"title"]; [note setObject:@"Note

    on Parse" forKey:@"content"]; [note save]; //[note saveInBackground]; //[note saveEventually]; Monday, July 2, 12
  34. [note saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { if (error) { NSLog(@"Note

    not saved"); } else { NSLog(@"Note saved successfully"); } }]; Monday, July 2, 12
  35. curl -X POST \ -H "X-Parse-Application-Id: ${APPLICATION_ID}" \ -H "X-Parse-REST-API-Key:

    ${REST_API_KEY}" \ -H "Content-Type: application/json" \ -d '{"note": 001, "title": "Ciao", "content": “Note on parse” }' \ https://api.parse.com/1/classes/GameScore Monday, July 2, 12
  36. PFObject *note = [PFObject objectWithClassName:@"Note"]; [note setObject:@"Ciao" forKey:@"title"]; [note setObject:@"Note

    on parse" forKey:@"content"]; PFObject *myTag = [PFObject objectWithClassName:@"Tag"]; [myTag setObject:@"important" forKey:@"tagName"]; // Add a relation [note setObject:myTag forKey:@"tag"]; // Saves both [note saveInBackground]; Monday, July 2, 12
  37. “You can’t always get what you want but if you

    try sometime, you just might find ...” Monday, July 2, 12
  38. “You can’t always get what you want but if you

    try sometime, you just might find ...” Rolling Stones Monday, July 2, 12