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

Session 227 - Using iCloud with Core Data

wwdcman
June 25, 2012
280

Session 227 - Using iCloud with Core Data

wwdcman

June 25, 2012
Tweet

Transcript

  1. These are confidential sessions—please refrain from streaming, blogging, or taking

    pictures Session 227 Using iCloud with Core Data Adam Swift Senior Software Engineer
  2. Three-Way Merge Preserve changes between systems John Doe johnny@.com Johnny

    Appleseed john@.com Johnny Doe john@.com John Doe john@.com
  3. Three-Way Merge Preserve changes between systems John Doe johnny@.com Johnny

    Appleseed john@.com Johnny Doe john@.com John Doe john@.com Johnny Appleseed johnny@.com
  4. What You Need •Xcode and OS X/iOS SDK ▪ Core

    Data project template (or Sample Code) ▪ iCloud Entitlements
  5. What You Need •Xcode and OS X/iOS SDK ▪ Core

    Data project template (or Sample Code) ▪ iCloud Entitlements •Provisioning Portal ▪ App ID for iCloud ▪ Provisioning Profile
  6. Where Does the Data Go? •Different types of apps •Where

    does app data live? ▪ All in iCloud ▪ Some in iCloud, some local
  7. Where Does the Data Go? •Different types of apps •Where

    does app data live? ▪ All in iCloud ▪ Some in iCloud, some local
  8. •Data goes into iCloud •Not Persistent Store file! What Goes

    into iCloud? Persistent Store Never open an SQLite database in iCloud
  9. •Data goes into iCloud •Not Persistent Store file! •Data transferred

    as incremental changes via iCloud What Goes into iCloud? Persistent Store
  10. Persistent Store as Cache •Data in iCloud •Access via Persistent

    Store •Rebuild from data in iCloud Persistent Store
  11. Fallback Store •No iCloud account? ▪ Provide seamless app experience

    ▪ Create local ‘fallback’ store •Existing store (pre-iCloud app version)
  12. Fallback Store •No iCloud account? ▪ Provide seamless app experience

    ▪ Create local ‘fallback’ store •Existing store (pre-iCloud app version) •Move store data into iCloud when enabled
  13. •Your decision •Determined by access needs ▪ Access requires iCloud

    account? ▪ Or, allow read-only access without account? iCloud-Enabled Store Location
  14. Requires iCloud account for access Put Store in iCloud Container

    •In container, but .nosync not transferred to iCloud
  15. Requires iCloud account for access Put Store in iCloud Container

    •In container, but .nosync not transferred to iCloud •Removed when the account changes ▪ Rebuild store from iCloud data
  16. Requires iCloud account for access Put Store in iCloud Container

    •In container, but .nosync not transferred to iCloud •Removed when the account changes ▪ Rebuild store from iCloud data •Simple ▪ Guaranteed to match “ubiquitous content” ▪ No iCloud? No store
  17. Keep Store in Sandbox •Persistent store in App Sandbox, data

    in iCloud •Store file survives account changes •One store per-iCloud account •Read-only access without account! ▪ Use NSReadOnlyPersistentStoreOption ▪ Optionally move data to fallback store Allows read-only access without account
  18. Partition Data •Two Persistent Stores ▪ One for local device

    data ▪ One enables data in iCloud •Use different models or configurations Local Store iCloud Enabled
  19. •Subset of entities from a data model •Create store with

    configuration ▪ Includes only those entities •One context can access stores with different configurations (based on the same model) Model Configurations “LocalConfig” “CloudConfig”
  20. •Subset of entities from a data model •Create store with

    configuration ▪ Includes only those entities •One context can access stores with different configurations (based on the same model) Model Configurations “LocalConfig” “CloudConfig”
  21. Configurations in Code cloudStore = [psc addPersistentStoreWithType: NSSQLiteStoreType configuration: @”CloudConfig”

    URL: cloudStoreURL options: cloudOptions error: &error]; localStore = [psc addPersistentStoreWithType: NSSQLiteStoreType configuration: @”LocalConfig” URL: localStoreURL options: localOptions error: &error];
  22. Goals •Get to full UI •Evaluate status ▪ Ready to

    go? ▪ Seed? ▪ Fallback? •Stay responsive
  23. Background Queue Add iCloud Store Seed Data Signed In Check

    iCloud Status ? •Launch Notify App Launch Launch UI
  24. Background Queue Add iCloud Store Seed Data Signed In Check

    iCloud Status ? •Launch Notify App Launch Full UI Launch UI
  25. Background Queue Add iCloud Store Seed Data Signed In Check

    iCloud Status ? •Launch Notify App Launch Full UI Launch UI
  26. Background Queue Add iCloud Store Seed Data Signed In No

    Account Check iCloud Status ? •Launch Notify App Launch Full UI Launch UI
  27. Background Queue Background Queue Add iCloud Store Seed Data Signed

    In No Account Check iCloud Status ? •Launch Notify App Launch Full UI Launch UI
  28. Background Queue Background Queue Add Fallback Store Prep Fallback Store

    Add iCloud Store Seed Data Signed In No Account Check iCloud Status ? Notify •Launch Notify App Launch Full UI Launch UI
  29. Background Queue Background Queue Add Fallback Store Prep Fallback Store

    Add iCloud Store Seed Data Signed In No Account Check iCloud Status ? Notify •Launch Notify App Launch Full UI Launch UI
  30. •Launch •Check iCloud Status -init // Get token for iCloud

    user account id currentToken = [fileManager ubiquityIdentityToken]; isiCloudSignedIn = (currentToken != nil);
  31. •Launch •Check iCloud Status -init // Get token for iCloud

    user account id currentToken = [fileManager ubiquityIdentityToken]; isiCloudSignedIn = (currentToken != nil);
  32. •Launch •Check iCloud Status •Add Persistent Store - (void)loadPersistentStores {

    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^(void) { [self asyncLoadPersistentStores]; }); } -loadPersistentStores
  33. •Launch •Check iCloud Status •Add Persistent Store - (BOOL)loadiCloudStore {

    NSFileManager *fm = [[NSFileManager alloc] init]; _ubiquityURL = [fm URLForUbiquityContainerIdentifier:nil]; iCloudStoreURL = [self iCloudStoreURL]; iCloudDataURL = [ubiquityURL URLByAppendingPathComponent:@"iCloudData"]; ... NSDictionary *options = @{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" NSPersistentStoreUbiquitousContentURLKey : iCloudDataURL }; [psc addPersistentStoreWithType: NSSQLiteStoreType configuration: @”CloudConfig” URL: iCloudStoreURL options: options error: &localError]; -loadiCloudStore
  34. •Launch •Check iCloud Status •Add Persistent Store - (BOOL)loadiCloudStore {

    NSFileManager *fm = [[NSFileManager alloc] init]; _ubiquityURL = [fm URLForUbiquityContainerIdentifier:nil]; iCloudStoreURL = [self iCloudStoreURL]; iCloudDataURL = [ubiquityURL URLByAppendingPathComponent:@"iCloudData"]; ... NSDictionary *options = @{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" NSPersistentStoreUbiquitousContentURLKey : iCloudDataURL }; [psc addPersistentStoreWithType: NSSQLiteStoreType configuration: @”CloudConfig” URL: iCloudStoreURL options: options error: &localError]; -loadiCloudStore
  35. •Launch •Check iCloud Status •Add Persistent Store - (BOOL)loadiCloudStore {

    NSFileManager *fm = [[NSFileManager alloc] init]; _ubiquityURL = [fm URLForUbiquityContainerIdentifier:nil]; iCloudStoreURL = [self iCloudStoreURL]; iCloudDataURL = [ubiquityURL URLByAppendingPathComponent:@"iCloudData"]; ... NSDictionary *options = @{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" NSPersistentStoreUbiquitousContentURLKey : iCloudDataURL }; [psc addPersistentStoreWithType: NSSQLiteStoreType configuration: @”CloudConfig” URL: iCloudStoreURL options: options error: &localError]; -loadiCloudStore
  36. •Launch •Check iCloud Status •Add Persistent Store - (BOOL)loadiCloudStore {

    NSFileManager *fm = [[NSFileManager alloc] init]; _ubiquityURL = [fm URLForUbiquityContainerIdentifier:nil]; iCloudStoreURL = [self iCloudStoreURL]; iCloudDataURL = [ubiquityURL URLByAppendingPathComponent:@"iCloudData"]; ... NSDictionary *options = @{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" NSPersistentStoreUbiquitousContentURLKey : iCloudDataURL }; [psc addPersistentStoreWithType: NSSQLiteStoreType configuration: @”CloudConfig” URL: iCloudStoreURL options: options error: &localError]; -loadiCloudStore
  37. •Launch •Check iCloud Status •Add Persistent Store - (BOOL)loadiCloudStore {

    NSFileManager *fm = [[NSFileManager alloc] init]; _ubiquityURL = [fm URLForUbiquityContainerIdentifier:nil]; iCloudStoreURL = [self iCloudStoreURL]; iCloudDataURL = [ubiquityURL URLByAppendingPathComponent:@"iCloudData"]; ... NSDictionary *options = @{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" NSPersistentStoreUbiquitousContentURLKey : iCloudDataURL }; [psc addPersistentStoreWithType: NSSQLiteStoreType configuration: @”CloudConfig” URL: iCloudStoreURL options: options error: &localError]; -loadiCloudStore
  38. •Launch •Check iCloud Status •Add Persistent Store - (BOOL)loadiCloudStore {

    NSFileManager *fm = [[NSFileManager alloc] init]; _ubiquityURL = [fm URLForUbiquityContainerIdentifier:nil]; iCloudStoreURL = [self iCloudStoreURL]; iCloudDataURL = [ubiquityURL URLByAppendingPathComponent:@"iCloudData"]; ... NSDictionary *options = @{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" NSPersistentStoreUbiquitousContentURLKey : iCloudDataURL }; [psc addPersistentStoreWithType: NSSQLiteStoreType configuration: @”CloudConfig” URL: iCloudStoreURL options: options error: &localError]; -loadiCloudStore
  39. •Launch •Check iCloud Status •Add Persistent Store •Seed Initial Data

    - (void)asyncLoadPersistentStores { ... if ([self loadiCloudStore]) { ... [self seedPersistentStore:_iCloudStore withPersistentStoreAtURL:[self seedStoreURL] error:&error]; } } -asyncLoadPersistenStores
  40. •Launch •Check iCloud Status •Add Persistent Store •Seed Initial Data

    •Notify Store Ready [[NSNotificationCenter defaultCenter] addObserver: rootViewController selector: @selector(reloadFetchedResults:) name: NSPersistentStoreCoordinatorStoresDidChangeNotification object: psc]; -application:didFinishLaunchingWithOptions:
  41. •Launch •Check iCloud Status •Add Persistent Store •Seed Initial Data

    •Notify Store Ready [[NSNotificationCenter defaultCenter] addObserver: rootViewController selector: @selector(reloadFetchedResults:) name: NSPersistentStoreCoordinatorStoresDidChangeNotification object: psc]; -application:didFinishLaunchingWithOptions:
  42. Seeding •Add Seed Store •Migrate objects -seedPersistentStoreStore: withPersistentStoreAtURL: error: NSUInteger

    batchSize = 500; [fr setFetchBatchSize:batchSize]; seedObjs = [moc executeFetchRequest:fr error:&error];
  43. Seeding •Add Seed Store •Migrate objects -seedPersistentStoreStore: withPersistentStoreAtURL: error: NSUInteger

    batchSize = 500; [fr setFetchBatchSize:batchSize]; seedObjs = [moc executeFetchRequest:fr error:&error]; for (NSManagedObject *obj in seedObjs) { [self addManagedObjectToiCloudStore:obj]; if (0 == (i % batchSize)) { if ([moc save:&error]) { [moc reset]; } } i++; }
  44. Uniquing •Find the Duplicates NSAttributeDescription *emailAttr; NSFetchRequest *fr; [fr setPropertiesToFetch:[NSArray

    arrayWithObjects:emailAttr, countExpr, nil]]; [fr setPropertiesToGroupBy:[NSArray arrayWithObject:emailAttr]]; NSExpression *countExpr = [NSExpression expressionWithFormat:@"count:(emailAddress)"]; -deDupe:
  45. Uniquing •Find the Duplicates NSAttributeDescription *emailAttr; NSFetchRequest *fr; [fr setPropertiesToFetch:[NSArray

    arrayWithObjects:emailAttr, countExpr, nil]]; [fr setPropertiesToGroupBy:[NSArray arrayWithObject:emailAttr]]; NSExpression *countExpr = [NSExpression expressionWithFormat:@"count:(emailAddress)"]; [fr setResultType:NSDictionaryResultType]; -deDupe:
  46. Uniquing •Find the Duplicates 2012-06-04 15:41:38.736 SharedCoreData[26470:10d03] CoreData: sql: SELECT

    t0.ZEMAILADDRESS, COUNT( t0.ZEMAILADDRESS) FROM ZPERSON t0 GROUP BY t0.ZEMAILADDRESS NSArray *countDictionaries = [moc executeFetchRequest:fr error:&error]; -deDupe:
  47. Uniquing •Find the Duplicates (lldb) po countDictionaries (NSArray *) $2

    = 0x07c0cb80 <_PFArray 0x7c0cb80>( { count = 1; emailAddress = "[email protected]"; }, { count = 256; emailAddress = "[email protected]"; }, { count = 1; emailAddress = "[email protected]"; }) 2012-06-04 15:41:38.736 SharedCoreData[26470:10d03] CoreData: sql: SELECT t0.ZEMAILADDRESS, COUNT( t0.ZEMAILADDRESS) FROM ZPERSON t0 GROUP BY t0.ZEMAILADDRESS NSArray *countDictionaries = [moc executeFetchRequest:fr error:&error]; -deDupe:
  48. Uniquing •Find the Duplicates •Fetch Duplicate Objects p = [NSPredicate

    predicateWithFormat:@"emailAddress IN (%@)", emailsWithDupes]; [fr setPredicate:p]; -deDupe:
  49. Uniquing •Find the Duplicates •Fetch Duplicate Objects emailSort = [NSSortDescriptor

    sortDescriptorWithKey:@"emailAddress" ascending:YES]; [fr setSortDescriptors:[NSArray arrayWithObject:emailSort]]; p = [NSPredicate predicateWithFormat:@"emailAddress IN (%@)", emailsWithDupes]; [fr setPredicate:p]; NSArray *dupes = [moc executeFetchRequest:fr error:&error]; -deDupe:
  50. Uniquing •Find the Duplicates •Fetch Duplicate Objects ▪ Ensure consistent

    merges across peers ▪ Use a record UUID or timestamp •Choose a winner -deDupe:
  51. Uniquing •Find the Duplicates •Fetch Duplicate Objects ▪ Ensure consistent

    merges across peers ▪ Use a record UUID or timestamp for (NSManagedObject *dupe in duplicates) { //choose winner if (0 == (i % batchSize)) { [moc save:&error]; } i++; } •Choose a winner -deDupe:
  52. Handling Account Changes User Events •NSFileManager API NSFileManager -ubiquityIdentityToken -

    (void)applicationDidBecomeActive:(UIApplication *)application { id token = [[NSFileManager defaultManager] ubiquityIdentityToken]; if (![self.currentUbiquityToken isEqual:token]) { [self iCloudAccountChanged:nil]; } } -iCloudAccountChanged:
  53. Handling Account Changes User Events •NSFileManager API NSFileManager -ubiquityIdentityToken NSUbiquityIdentityDidChangeNotification

    [notificationCenter addObserver:self selector:@selector(iCloudAccountChanged:) name:NSUbiquityIdentityDidChangeNotification object:nil]; -iCloudAccountChanged:
  54. - (void)iCloudAccountChanged:(NSNotification *)notification { NSError *error = nil; [_psc removePersistentStore:self.iCloudStore

    error:&error]; [self loadPersistentStores]; } Handling Account Changes User Events -iCloudAccountChanged:
  55. Debugging •Macs are greedy peers ▪ Run at least one

    iCloud-enabled app (TextEdit) •Removing data ▪ Coordinated write to delete every file inside container ▪ Be patient, it might take a while to propagate to all your devices
  56. Debugging •Macs are greedy peers ▪ Run at least one

    iCloud-enabled app (TextEdit) •Removing data ▪ Coordinated write to delete every file inside container ▪ Be patient, it might take a while to propagate to all your devices •File great bugs
  57. More Information Michael Jurewitz Technology Evangelist [email protected] Cocoa Feedback [email protected]

    Core Data Documentation Programming Guides, Examples, Tutorials http://developer.apple.com/ Apple Developer Forums http://devforums.apple.com
  58. Related Sessions Advanced iCloud Document Storage Marina Thursday 3:15PM Core

    Data Best Practices Mission Wednesday 9:00AM iCloud Storage Overview Pacific Heights Tuesday 4:30PM
  59. Labs Core Data Lab Developer Tools Lab A Thursday 9:00AM

    iCloud Storage Lab Essentials Lab B Thursday 4:30PM Core Data Lab Essentials Lab B Friday 9:00AM iCloud Storage Lab Essentials Lab B Friday 11:30AM