diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.h b/MasterPassword/ObjC/MPAppDelegate_Store.h index 742eb3ab..d45a8610 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.h +++ b/MasterPassword/ObjC/MPAppDelegate_Store.h @@ -18,7 +18,7 @@ typedef enum { MPImportResultInternalError, } MPImportResult; -@interface MPAppDelegate_Shared (Store)// +@interface MPAppDelegate_Shared (Store) + (NSManagedObjectContext *)managedObjectContextForThreadIfReady; + (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *moc))mocBlock; diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.m b/MasterPassword/ObjC/MPAppDelegate_Store.m index d21d0128..cbc339e5 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.m +++ b/MasterPassword/ObjC/MPAppDelegate_Store.m @@ -68,24 +68,24 @@ static char privateManagedObjectContextKey, mainManagedObjectContextKey; - (NSManagedObjectContext *)privateManagedObjectContextIfReady { NSManagedObjectContext *privateManagedObjectContext = objc_getAssociatedObject(self, &privateManagedObjectContextKey); -// if (!privateManagedObjectContext) { -// privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; -// [privateManagedObjectContext performBlockAndWait:^{ -// privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; -// privateManagedObjectContext.persistentStoreCoordinator = self.storeManager.persistentStoreCoordinator; -// }]; -// -// NSManagedObjectContext *mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; -// mainManagedObjectContext.parentContext = privateManagedObjectContext; -// -// objc_setAssociatedObject(self, &privateManagedObjectContextKey, privateManagedObjectContext, OBJC_ASSOCIATION_RETAIN); -// objc_setAssociatedObject(self, &mainManagedObjectContextKey, mainManagedObjectContext, OBJC_ASSOCIATION_RETAIN); -// } -// -// if (![privateManagedObjectContext.persistentStoreCoordinator.persistentStores count]) -// // Store not available yet. -// return nil; -// + if (!privateManagedObjectContext) { + privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; + [privateManagedObjectContext performBlockAndWait:^{ + privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; + privateManagedObjectContext.persistentStoreCoordinator = self.storeManager.persistentStoreCoordinator; + }]; + + NSManagedObjectContext *mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; + mainManagedObjectContext.parentContext = privateManagedObjectContext; + + objc_setAssociatedObject(self, &privateManagedObjectContextKey, privateManagedObjectContext, OBJC_ASSOCIATION_RETAIN); + objc_setAssociatedObject(self, &mainManagedObjectContextKey, mainManagedObjectContext, OBJC_ASSOCIATION_RETAIN); + } + + if (![privateManagedObjectContext.persistentStoreCoordinator.persistentStores count]) + // Store not available yet. + return nil; + return privateManagedObjectContext; } @@ -213,57 +213,57 @@ static char privateManagedObjectContextKey, mainManagedObjectContextKey; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"iCloudEnabledKey"]; } -//- (UbiquityStoreManager *)storeManager { -// -// static UbiquityStoreManager *storeManager = nil; -// if (storeManager) -// return storeManager; -// -// storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil -// containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared" -//#if TARGET_OS_IPHONE -// additionalStoreOptions:@{ -// NSPersistentStoreFileProtectionKey : NSFileProtectionComplete -// }]; -//#else -// additionalStoreOptions:nil]; -//#endif -// storeManager.delegate = self; -// -// // Migrate old store to new store location. -// [self migrateStoreForManager:storeManager]; -// -// [[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification -// object:storeManager queue:nil -// usingBlock:^(NSNotification *note) { -// objc_setAssociatedObject(self, &privateManagedObjectContextKey, nil, OBJC_ASSOCIATION_RETAIN); -// }]; -// [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:nil usingBlock: -// ^(NSNotification *note) { -// if ([[MPConfig get].iCloud boolValue] != [self.storeManager cloudEnabled]) -// self.storeManager.cloudEnabled = [[MPConfig get].iCloud boolValue]; -// }]; -//#if TARGET_OS_IPHONE -// [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification -// object:[UIApplication sharedApplication] queue:nil -// usingBlock:^(NSNotification *note) { -// [self saveContexts]; -// }]; -// [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification -// object:[UIApplication sharedApplication] queue:nil -// usingBlock:^(NSNotification *note) { -// [self saveContexts]; -// }]; -//#else -// [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillTerminateNotification -// object:[NSApplication sharedApplication] queue:nil -// usingBlock:^(NSNotification *note) { -// [self saveContexts]; -// }]; -//#endif -// -// return storeManager; -//} +- (UbiquityStoreManager *)storeManager { + + static UbiquityStoreManager *storeManager = nil; + if (storeManager) + return storeManager; + + storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil + containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared" +#if TARGET_OS_IPHONE + additionalStoreOptions:@{ + NSPersistentStoreFileProtectionKey : NSFileProtectionComplete + }]; +#else + additionalStoreOptions:nil]; +#endif + storeManager.delegate = self; + + // Migrate old store to new store location. + [self migrateStoreForManager:storeManager]; + + [[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification + object:storeManager queue:nil + usingBlock:^(NSNotification *note) { + objc_setAssociatedObject(self, &privateManagedObjectContextKey, nil, OBJC_ASSOCIATION_RETAIN); + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:nil usingBlock: + ^(NSNotification *note) { + if ([[MPConfig get].iCloud boolValue] != [self.storeManager cloudEnabled]) + self.storeManager.cloudEnabled = [[MPConfig get].iCloud boolValue]; + }]; +#if TARGET_OS_IPHONE + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification + object:[UIApplication sharedApplication] queue:nil + usingBlock:^(NSNotification *note) { + [self saveContexts]; + }]; + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification + object:[UIApplication sharedApplication] queue:nil + usingBlock:^(NSNotification *note) { + [self saveContexts]; + }]; +#else + [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillTerminateNotification + object:[NSApplication sharedApplication] queue:nil + usingBlock:^(NSNotification *note) { + [self saveContexts]; + }]; +#endif + + return storeManager; +} - (void)saveContexts { @@ -312,71 +312,71 @@ static char privateManagedObjectContextKey, mainManagedObjectContextKey; [MPConfig get].iCloud = @(cloudEnabled); } -//- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didEncounterError:(NSError *)error cause:(UbiquityStoreManagerErrorCause)cause -// context:(id)context { -// -// err(@"StoreManager: cause=%d, context=%@, error=%@", cause, context, error); -// -//#ifdef TESTFLIGHT_SDK_VERSION -// [TestFlight passCheckpoint:PearlString(MPCheckpointMPErrorUbiquity @"_%d", cause)]; -//#endif -//#ifdef LOCALYTICS -// [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointMPErrorUbiquity attributes:@{ -// @"cause": @(cause), -// @"error.domain": error.domain, -// @"error.code": @(error.code) -// }]; -//#endif -// -// switch (cause) { -// case UbiquityStoreManagerErrorCauseDeleteStore: -// case UbiquityStoreManagerErrorCauseCreateStorePath: -// case UbiquityStoreManagerErrorCauseClearStore: -// break; -// case UbiquityStoreManagerErrorCauseOpenLocalStore: { -// wrn(@"Local store could not be opened: %@", error); -// -// if (error.code == NSMigrationMissingSourceModelError) { -// wrn(@"Resetting the local store."); -// -//#ifdef TESTFLIGHT_SDK_VERSION -// [TestFlight passCheckpoint:MPCheckpointLocalStoreReset]; -//#endif -//#ifdef LOCALYTICS -// [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointLocalStoreReset attributes:nil]; -//#endif -// [manager deleteLocalStore]; -// -// Throw(@"Local store was reset, application must be restarted to use it."); -// } else -// // Try again. -// [manager persistentStoreCoordinator]; -// } -// case UbiquityStoreManagerErrorCauseOpenCloudStore: { -// wrn(@"iCloud store could not be opened: %@", error); -// -// if (error.code == NSMigrationMissingSourceModelError) { -// wrn(@"Resetting the iCloud store."); -// -//#ifdef TESTFLIGHT_SDK_VERSION -// [TestFlight passCheckpoint:MPCheckpointCloudStoreReset]; -//#endif -//#ifdef LOCALYTICS -// [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloudStoreReset attributes:nil]; -//#endif -// [manager deleteCloudStore]; -// break; -// } else -// // Try again. -// [manager persistentStoreCoordinator]; -// } -// case UbiquityStoreManagerErrorCauseMigrateLocalToCloudStore: { -// wrn(@"Couldn't migrate local store to the cloud: %@", error); -// wrn(@"Resetting the iCloud store."); -// [manager deleteCloudStore]; -// }; -// } -//} +- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didEncounterError:(NSError *)error cause:(UbiquityStoreManagerErrorCause)cause + context:(id)context { + + err(@"StoreManager: cause=%d, context=%@, error=%@", cause, context, error); + +#ifdef TESTFLIGHT_SDK_VERSION + [TestFlight passCheckpoint:PearlString(MPCheckpointMPErrorUbiquity @"_%d", cause)]; +#endif +#ifdef LOCALYTICS + [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointMPErrorUbiquity attributes:@{ + @"cause": @(cause), + @"error.domain": error.domain, + @"error.code": @(error.code) + }]; +#endif + + switch (cause) { + case UbiquityStoreManagerErrorCauseDeleteStore: + case UbiquityStoreManagerErrorCauseCreateStorePath: + case UbiquityStoreManagerErrorCauseClearStore: + break; + case UbiquityStoreManagerErrorCauseOpenLocalStore: { + wrn(@"Local store could not be opened: %@", error); + + if (error.code == NSMigrationMissingSourceModelError) { + wrn(@"Resetting the local store."); + +#ifdef TESTFLIGHT_SDK_VERSION + [TestFlight passCheckpoint:MPCheckpointLocalStoreReset]; +#endif +#ifdef LOCALYTICS + [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointLocalStoreReset attributes:nil]; +#endif + [manager deleteLocalStore]; + + Throw(@"Local store was reset, application must be restarted to use it."); + } else + // Try again. + [manager persistentStoreCoordinator]; + } + case UbiquityStoreManagerErrorCauseOpenCloudStore: { + wrn(@"iCloud store could not be opened: %@", error); + + if (error.code == NSMigrationMissingSourceModelError) { + wrn(@"Resetting the iCloud store."); + +#ifdef TESTFLIGHT_SDK_VERSION + [TestFlight passCheckpoint:MPCheckpointCloudStoreReset]; +#endif +#ifdef LOCALYTICS + [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloudStoreReset attributes:nil]; +#endif + [manager deleteCloudStore]; + break; + } else + // Try again. + [manager persistentStoreCoordinator]; + } + case UbiquityStoreManagerErrorCauseMigrateLocalToCloudStore: { + wrn(@"Couldn't migrate local store to the cloud: %@", error); + wrn(@"Resetting the iCloud store."); + [manager deleteCloudStore]; + }; + } +} #pragma mark - Import / Export diff --git a/MasterPassword/ObjC/iOS/MPAppDelegate.m b/MasterPassword/ObjC/iOS/MPAppDelegate.m index 8e0ebcaa..0cb7fcc1 100644 --- a/MasterPassword/ObjC/iOS/MPAppDelegate.m +++ b/MasterPassword/ObjC/iOS/MPAppDelegate.m @@ -616,49 +616,49 @@ #pragma mark - UbiquityStoreManagerDelegate -//- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToCloud:(BOOL)cloudEnabled { -// -// [super ubiquityStoreManager:manager didSwitchToCloud:cloudEnabled]; -// -// if (![[MPConfig get].iCloudDecided boolValue]) { -// if (!cloudEnabled) { -// [PearlAlert showAlertWithTitle:@"iCloud" -// message: -// @"iCloud is now disabled.\n\n" -// @"It is highly recommended you enable iCloud." -// viewStyle:UIAlertViewStyleDefault initAlert:nil -// tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { -// if (buttonIndex == [alert firstOtherButtonIndex] + 0) { -// [PearlAlert showAlertWithTitle:@"About iCloud" -// message: -// @"iCloud is Apple's solution for saving your data in \"the cloud\" " -// @"and making sure your other iPhones, iPads and Macs are in sync.\n\n" -// @"For Master Password, that means your sites are available on all your " -// @"Apple devices, and you always have a backup of them in case " -// @"you loose one or need to restore.\n\n" -// @"Because of the way Master Password works, it doesn't need to send your " -// @"site's passwords to Apple. Only their names are saved to make it easier " -// @"for you to find the site you need. For some sites you may have set " -// @"a user-specified password: these are sent to iCloud after being encrypted " -// @"with your master password.\n\n" -// @"Apple can never see any of your passwords." -// viewStyle:UIAlertViewStyleDefault -// initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { -// [self ubiquityStoreManager:manager didSwitchToCloud:cloudEnabled]; -// } -// cancelTitle:[PearlStrings get].commonButtonThanks otherTitles:nil]; -// return; -// } -// -// [MPConfig get].iCloudDecided = @YES; -// if (buttonIndex == [alert cancelButtonIndex]) -// return; -// if (buttonIndex == [alert firstOtherButtonIndex] + 1) -// manager.cloudEnabled = YES; -// } cancelTitle:@"Leave iCloud Off" otherTitles:@"Explain?", @"Enable iCloud", nil]; -// } -// } -//} +- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToCloud:(BOOL)cloudEnabled { + + [super ubiquityStoreManager:manager didSwitchToCloud:cloudEnabled]; + + if (![[MPConfig get].iCloudDecided boolValue]) { + if (!cloudEnabled) { + [PearlAlert showAlertWithTitle:@"iCloud" + message: + @"iCloud is now disabled.\n\n" + @"It is highly recommended you enable iCloud." + viewStyle:UIAlertViewStyleDefault initAlert:nil + tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { + if (buttonIndex == [alert firstOtherButtonIndex] + 0) { + [PearlAlert showAlertWithTitle:@"About iCloud" + message: + @"iCloud is Apple's solution for saving your data in \"the cloud\" " + @"and making sure your other iPhones, iPads and Macs are in sync.\n\n" + @"For Master Password, that means your sites are available on all your " + @"Apple devices, and you always have a backup of them in case " + @"you loose one or need to restore.\n\n" + @"Because of the way Master Password works, it doesn't need to send your " + @"site's passwords to Apple. Only their names are saved to make it easier " + @"for you to find the site you need. For some sites you may have set " + @"a user-specified password: these are sent to iCloud after being encrypted " + @"with your master password.\n\n" + @"Apple can never see any of your passwords." + viewStyle:UIAlertViewStyleDefault + initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) { + [self ubiquityStoreManager:manager didSwitchToCloud:cloudEnabled]; + } + cancelTitle:[PearlStrings get].commonButtonThanks otherTitles:nil]; + return; + } + + [MPConfig get].iCloudDecided = @YES; + if (buttonIndex == [alert cancelButtonIndex]) + return; + if (buttonIndex == [alert firstOtherButtonIndex] + 1) + manager.cloudEnabled = YES; + } cancelTitle:@"Leave iCloud Off" otherTitles:@"Explain?", @"Enable iCloud", nil]; + } + } +} #pragma mark - Google+