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

Beginning iCloud Development

Beginning iCloud Development

I will describe how iCloud works, what it is meant for, best practices to set up a project and implement synchronization strategies to replicate content on iOS and MacOSX devices.

http://mdevcon.com/2012/01/10/cesare-rocchi/

Cesare Rocchi

March 12, 2012
Tweet

More Decks by Cesare Rocchi

Other Decks in Programming

Transcript

  1. @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; }
  2. @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 { }
  3. #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
  4. - (void)loadDocument { NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; _query

    = query; [query setSearchScopes:[NSArray arrayWithObject: NSMetadataQueryUbiquitousDocumentsScope]]; }
  5. - (void)loadDocument { NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; _query

    = query; [query setSearchScopes:[NSArray arrayWithObject: NSMetadataQueryUbiquitousDocumentsScope]]; NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, kFILENAME]; [query setPredicate:pred]; }
  6. - (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]; }
  7. - (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]; }
  8. - (void)loadData:(NSMetadataQuery *)query { if ([query resultCount] == 1) {

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

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

    = [[ubiq URLByAppendingPathComponent: @"Documents"] URLByAppendingPathComponent:kFILENAME]; SMNote *doc = [[SMNote alloc] initWithFileURL:ubiquitousPackage]; }
  11. 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"); }]; } }]; }
  12. - (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; }
  13. - (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; }
  14. - (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; }
  15. - (NSURL *) localNotesURL { return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]

    lastObject]; } - (NSURL *) ubiquitousNotesURL { return [[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] URLByAppendingPathComponent:@"Documents"]; }
  16. - (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!
  17. - (void) startMigration { NSOperationQueue *iCloudQueue = [NSOperationQueue new]; NSInvocationOperation

    *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(setNoteUbiquity) object:nil]; [iCloudQueue addOperation:op]; }
  18. @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
  19. #import "SMNote.h" @interface SMNotesDocument : UIDocument @property (nonatomic, strong) NSMutableArray

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

    *entries; @property (nonatomic, strong) NSFileWrapper *fileWrapper; @end
  21. - (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]; }
  22. - (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; }
  23. - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError { NSFileWrapper *wrapper =

    (NSFileWrapper *)contents; NSDictionary *d = [wrapper fileWrappers]; NSFileWrapper *entriesWrap = [d objectForKey:@"notes.dat"]; }
  24. - (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 }
  25. - (void) saveNoteAsCurrent { [[NSUbiquitousKeyValueStore defaultStore] setString:self.currentNote.noteId forKey:@"com.studiomagnolia.currentNote"]; [[NSUbiquitousKeyValueStore defaultStore]

    synchronize]; } NSString *currentNoteId = [[NSUbiquitousKeyValueStore defaultStore] stringForKey: @"com.studiomagnolia.currentNote"];
  26. 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; }
  27. PFObject *note = [PFObject objectWithClassName:@"Note"]; [note setObject:@"Ciao" forKey:@"title"]; [note setObject:@"Note

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

    not saved"); } else { NSLog(@"Note saved successfully"); } }];
  29. 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
  30. 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];