diff --git a/External/Pearl b/External/Pearl index 5d814a47..5b51c336 160000 --- a/External/Pearl +++ b/External/Pearl @@ -1 +1 @@ -Subproject commit 5d814a47ed07355077a7d292d5f8459625f137b3 +Subproject commit 5b51c3360c5e173390678899f99ab89c903465de diff --git a/External/UbiquityStoreManager b/External/UbiquityStoreManager index d399ae61..22dabd3a 160000 --- a/External/UbiquityStoreManager +++ b/External/UbiquityStoreManager @@ -1 +1 @@ -Subproject commit d399ae61e8610ed2f7ac93a5104517729cc6f981 +Subproject commit 22dabd3ad6f3f04bc747aebffd46a57c7f28aabd diff --git a/MasterPassword/ObjC/MPAppDelegate_Key.m b/MasterPassword/ObjC/MPAppDelegate_Key.m index 7ede1204..ce3cd8dd 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Key.m +++ b/MasterPassword/ObjC/MPAppDelegate_Key.m @@ -144,7 +144,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) { } [user saveContext]; #ifdef PEARL_UIKIT - [activityAlert dismissAlert]; + [activityAlert cancelAlert]; #endif } } diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.h b/MasterPassword/ObjC/MPAppDelegate_Store.h index d45a8610..0f0869c9 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.h +++ b/MasterPassword/ObjC/MPAppDelegate_Store.h @@ -24,6 +24,7 @@ typedef enum { + (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *moc))mocBlock; + (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *))mocBlock; +- (UbiquityStoreManager *)storeManager; - (MPImportResult)importSites:(NSString *)importedSitesString askImportPassword:(NSString *(^)(NSString *userName))importPassword askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword; diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.m b/MasterPassword/ObjC/MPAppDelegate_Store.m index cbc339e5..e9aa6719 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.m +++ b/MasterPassword/ObjC/MPAppDelegate_Store.m @@ -9,6 +9,14 @@ #import #import "MPAppDelegate_Store.h" +@interface MPAppDelegate_Shared () + +@property(nonatomic, strong) PearlAlert *handleCloudContentAlert; +@property(nonatomic, strong) PearlAlert *fixCloudContentAlert; +@property(nonatomic, strong) PearlOverlay *storeLoading; + +@end + @implementation MPAppDelegate_Shared (Store) static char privateManagedObjectContextKey, mainManagedObjectContextKey; @@ -58,35 +66,15 @@ static char privateManagedObjectContextKey, mainManagedObjectContextKey; } - (NSManagedObjectContext *)mainManagedObjectContextIfReady { - - if (![self privateManagedObjectContextIfReady]) - return nil; - - return objc_getAssociatedObject(self, &mainManagedObjectContextKey); + + [self storeManager]; + return objc_getAssociatedObject( self, &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; - - return privateManagedObjectContext; + [self storeManager]; + return objc_getAssociatedObject( self, &privateManagedObjectContextKey ); } - (void)migrateStoreForManager:(UbiquityStoreManager *)storeManager { @@ -223,26 +211,16 @@ static char privateManagedObjectContextKey, mainManagedObjectContextKey; containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared" #if TARGET_OS_IPHONE additionalStoreOptions:@{ - NSPersistentStoreFileProtectionKey : NSFileProtectionComplete - }]; + NSPersistentStoreFileProtectionKey : NSFileProtectionComplete + } #else - additionalStoreOptions:nil]; + additionalStoreOptions:nil #endif - storeManager.delegate = self; + 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 @@ -294,88 +272,97 @@ static char privateManagedObjectContextKey, mainManagedObjectContextKey; dbg(@"[StoreManager] %@", message); } -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToCloud:(BOOL)cloudEnabled { +- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore { - // manager.cloudEnabled is more reliable (eg. iOS' MPAppDelegate tampers with didSwitch a bit) - cloudEnabled = manager.cloudEnabled; - inf(@"Using iCloud? %@", cloudEnabled? @"YES": @"NO"); + if (![self.storeLoading isVisible]) + self.storeLoading = [PearlOverlay showOverlayWithTitle:@"Loading..."]; + + objc_setAssociatedObject( self, &privateManagedObjectContextKey, nil, OBJC_ASSOCIATION_RETAIN ); + objc_setAssociatedObject( self, &mainManagedObjectContextKey, nil, OBJC_ASSOCIATION_RETAIN ); +} + +- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didLoadStoreForCoordinator:(NSPersistentStoreCoordinator *)coordinator + isCloud:(BOOL)isCloudStore { + + inf(@"Using iCloud? %@", @(isCloudStore)); #ifdef TESTFLIGHT_SDK_VERSION - [TestFlight passCheckpoint:cloudEnabled? MPCheckpointCloudEnabled: MPCheckpointCloudDisabled]; + [TestFlight passCheckpoint:isCloudStore? MPCheckpointCloudEnabled: MPCheckpointCloudDisabled]; #endif #ifdef LOCALYTICS [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloud attributes:@{ - @"enabled": cloudEnabled? @"YES": @"NO" + @"enabled": isCloudStore? @"YES": @"NO" }]; #endif - [MPConfig get].iCloud = @(cloudEnabled); + // Create our contexts. + NSManagedObjectContext *privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; + [privateManagedObjectContext performBlockAndWait:^{ + privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; + privateManagedObjectContext.persistentStoreCoordinator = coordinator; + }]; + + 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 ); + + [self.handleCloudContentAlert cancelAlert]; + [self.fixCloudContentAlert cancelAlert]; + [self.storeLoading cancelOverlay]; } -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didEncounterError:(NSError *)error cause:(UbiquityStoreManagerErrorCause)cause +- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didEncounterError:(NSError *)error cause:(UbiquityStoreErrorCause)cause context:(id)context { - err(@"StoreManager: cause=%d, context=%@, error=%@", cause, context, error); + err(@"[StoreManager] ERROR: cause=%d, context=%@, error=%@", cause, context, error); #ifdef TESTFLIGHT_SDK_VERSION - [TestFlight passCheckpoint:PearlString(MPCheckpointMPErrorUbiquity @"_%d", cause)]; + [TestFlight passCheckpoint:PearlString( MPCheckpointMPErrorUbiquity @"_%d", cause )]; #endif #ifdef LOCALYTICS [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointMPErrorUbiquity attributes:@{ - @"cause": @(cause), - @"error.domain": error.domain, - @"error.code": @(error.code) + @"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); +- (BOOL)ubiquityStoreManager:(UbiquityStoreManager *)manager handleCloudContentCorruptionWithHealthyStore:(BOOL)storeHealthy { - if (error.code == NSMigrationMissingSourceModelError) { - wrn(@"Resetting the local store."); + if (manager.cloudEnabled && !storeHealthy && !([self.handleCloudContentAlert.alertView isVisible] || [self.fixCloudContentAlert.alertView isVisible])) + dispatch_async( dispatch_get_main_queue(), ^{ + [self showCloudContentAlert]; + } ); -#ifdef TESTFLIGHT_SDK_VERSION - [TestFlight passCheckpoint:MPCheckpointLocalStoreReset]; -#endif -#ifdef LOCALYTICS - [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointLocalStoreReset attributes:nil]; -#endif - [manager deleteLocalStore]; + return NO; +} - 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); +- (void)showCloudContentAlert { - if (error.code == NSMigrationMissingSourceModelError) { - wrn(@"Resetting the iCloud store."); + __weak MPAppDelegate_Shared *wSelf = self; + [self.handleCloudContentAlert cancelAlert]; + self.handleCloudContentAlert = [PearlAlert showActivityWithTitle:@"iCloud Sync Problem" message: + @"Waiting for your other device to auto‑correct the problem..." + initAlert:^(UIAlertView *alert) { + [alert addButtonWithTitle:@"Fix Now"]; + }]; -#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]; - }; - } + self.handleCloudContentAlert.tappedButtonBlock = ^(UIAlertView *alert, NSInteger buttonIndex) { + wSelf.fixCloudContentAlert = [PearlAlert showAlertWithTitle:@"Fix iCloud Now" message: + @"This problem can usually be auto‑corrected by opening the app on another device where you recently made changes.\n" + @"You can correct the problem from this device anyway, but recent changes made on another device might get lost." + viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock: + ^(UIAlertView *alert_, NSInteger buttonIndex_) { + if (buttonIndex_ == alert_.cancelButtonIndex) + [wSelf showCloudContentAlert]; + if (buttonIndex_ == [alert_ firstOtherButtonIndex]) + [wSelf.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:YES]; + } + cancelTitle:[PearlStrings get].commonButtonBack otherTitles:@"Fix Anyway", nil]; + }; } #pragma mark - Import / Export diff --git a/MasterPassword/ObjC/MPConfig.h b/MasterPassword/ObjC/MPConfig.h index 80472c82..8ef526e8 100644 --- a/MasterPassword/ObjC/MPConfig.h +++ b/MasterPassword/ObjC/MPConfig.h @@ -13,7 +13,6 @@ @property (nonatomic, retain) NSNumber *sendInfo; @property (nonatomic, retain) NSNumber *rememberLogin; -@property (nonatomic, retain) NSNumber *iCloud; @property (nonatomic, retain) NSNumber *iCloudDecided; @end diff --git a/MasterPassword/ObjC/MPConfig.m b/MasterPassword/ObjC/MPConfig.m index 43009015..7e323ba0 100644 --- a/MasterPassword/ObjC/MPConfig.m +++ b/MasterPassword/ObjC/MPConfig.m @@ -9,7 +9,7 @@ #import "MPAppDelegate.h" @implementation MPConfig -@dynamic sendInfo, rememberLogin, iCloud, iCloudDecided; +@dynamic sendInfo, rememberLogin, iCloudDecided; - (id)init { @@ -20,7 +20,6 @@ NSStringFromSelector(@selector(sendInfo)): @NO, NSStringFromSelector(@selector(rememberLogin)): @NO, - NSStringFromSelector(@selector(iCloud)): @NO, NSStringFromSelector(@selector(iCloudDecided)): @NO}]; self.delegate = [MPAppDelegate get]; diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword (App Store).xcscheme b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword Mac (App Store).xcscheme similarity index 100% rename from MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword (App Store).xcscheme rename to MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword Mac (App Store).xcscheme diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword Mac (Development).xcscheme similarity index 100% rename from MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme rename to MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/xcshareddata/xcschemes/MasterPassword Mac (Development).xcscheme diff --git a/MasterPassword/ObjC/iOS/MPAppDelegate.m b/MasterPassword/ObjC/iOS/MPAppDelegate.m index 0cb7fcc1..b61d16fe 100644 --- a/MasterPassword/ObjC/iOS/MPAppDelegate.m +++ b/MasterPassword/ObjC/iOS/MPAppDelegate.m @@ -203,7 +203,7 @@ [PearlLogger get].printLevel = PearlLogLevelInfo; [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"]; - [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloud boolValue] forKey:@"iCloud"]; + [[Crashlytics sharedInstance] setBoolValue:[self storeManager].cloudEnabled forKey:@"iCloud"]; [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloudDecided boolValue] forKey:@"iCloudDecided"]; [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].sendInfo boolValue] forKey:@"sendInfo"]; [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"]; @@ -216,7 +216,7 @@ #ifdef TESTFLIGHT_SDK_VERSION [TestFlight addCustomEnvironmentInformation:[[MPConfig get].rememberLogin boolValue]? @"YES": @"NO" forKey:@"rememberLogin"]; - [TestFlight addCustomEnvironmentInformation:[[MPConfig get].iCloud boolValue]? @"YES": @"NO" forKey:@"iCloud"]; + [TestFlight addCustomEnvironmentInformation:[self storeManager].cloudEnabled? @"YES": @"NO" forKey:@"iCloud"]; [TestFlight addCustomEnvironmentInformation:[[MPConfig get].iCloudDecided boolValue]? @"YES": @"NO" forKey:@"iCloudDecided"]; [TestFlight addCustomEnvironmentInformation:[[MPiOSConfig get].sendInfo boolValue]? @"YES": @"NO" forKey:@"sendInfo"]; [TestFlight addCustomEnvironmentInformation:[[MPiOSConfig get].helpHidden boolValue]? @"YES": @"NO" forKey:@"helpHidden"]; @@ -233,7 +233,7 @@ #endif [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointConfig attributes:@{ @"rememberLogin" : [[MPConfig get].rememberLogin boolValue]? @"YES": @"NO", - @"iCloud" : [[MPConfig get].iCloud boolValue]? @"YES": @"NO", + @"iCloud" : [self storeManager].cloudEnabled? @"YES": @"NO", @"iCloudDecided" : [[MPConfig get].iCloudDecided boolValue]? @"YES": @"NO", @"sendInfo" : [[MPiOSConfig get].sendInfo boolValue]? @"YES": @"NO", @"helpHidden" : [[MPiOSConfig get].helpHidden boolValue]? @"YES": @"NO", @@ -281,7 +281,7 @@ // Google+ if ([self.googlePlus handleURL:url sourceApplication:sourceApplication annotation:annotation]) return YES; - + // Arbitrary URL to mpsites data. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSError *error; @@ -365,7 +365,7 @@ break; } - [activityAlert dismissAlert]; + [activityAlert cancelAlert]; }); return YES; @@ -615,66 +615,62 @@ } #pragma mark - UbiquityStoreManagerDelegate +- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore { -- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToCloud:(BOOL)cloudEnabled { + [super ubiquityStoreManager:manager willLoadStoreIsCloud:isCloudStore]; - [super ubiquityStoreManager:manager didSwitchToCloud:cloudEnabled]; + if (!isCloudStore && ![[MPConfig get].iCloudDecided boolValue]) + [self alertCloudDisabledForManager:manager]; +} - 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; +- (void)alertCloudDisabledForManager:(UbiquityStoreManager *)manager { + + [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 lose one or need to restore.\n\n" + @"Thanks to the way Master Password works, it doesn't need to send your " + @"site's passwords to Apple for the backup to work: Only their names are " + @"saved. If you set a custom password it will be 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 alertCloudDisabledForManager:manager]; } + 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]; - } - } + [MPConfig get].iCloudDecided = @YES; + if (buttonIndex == [alert firstOtherButtonIndex] + 1) + manager.cloudEnabled = YES; + } cancelTitle:@"Leave Off" otherTitles:@"Explain?", @"Enable iCloud", nil]; } #pragma mark - Google+ - (NSDictionary *)googlePlusInfo { - + static NSDictionary *googlePlusInfo = nil; if (googlePlusInfo == nil) googlePlusInfo = [[NSDictionary alloc] initWithContentsOfURL: [[NSBundle mainBundle] URLForResource:@"Google+" withExtension:@"plist"]]; - + return googlePlusInfo; } - (NSString *)googlePlusClientID { - + return NSNullToNil([[self googlePlusInfo] valueForKeyPath:@"ClientID"]); } diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj index fd6efa06..fb750196 100644 --- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -7,10 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + 93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.m */; }; 93D39262A8A97DB748213309 /* PearlEMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393BB973253D4BAAC84AA /* PearlEMail.m */; }; 93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; }; 93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; }; 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; }; + 93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3942A356B639724157982 /* PearlOverlay.h */; }; 93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; }; @@ -1108,9 +1110,11 @@ /* Begin PBXFileReference section */ 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; }; + 93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = ""; }; 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = ""; }; 93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = ""; }; 93D394077F8FAB8167647187 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; }; + 93D3942A356B639724157982 /* PearlOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlOverlay.h; sourceTree = ""; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = ""; }; 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = ""; }; 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = ""; }; @@ -3863,6 +3867,8 @@ DAFE462515039823003ABA7C /* Resources */, DAFE4A1115039824003ABA7C /* UIImage+PearlScaling.h */, DAFE4A1215039824003ABA7C /* UIImage+PearlScaling.m */, + 93D390FADEB325D8D54A957D /* PearlOverlay.m */, + 93D3942A356B639724157982 /* PearlOverlay.h */, ); path = "Pearl-UIKit"; sourceTree = ""; @@ -4067,6 +4073,7 @@ DACA2A811705EE91002C6C22 /* crypto_aesctr.h in Headers */, DACA2A821705EE91002C6C22 /* warn.h in Headers */, DACA2A831705EE91002C6C22 /* readpass.h in Headers */, + 93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -5295,6 +5302,7 @@ 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */, 93D39262A8A97DB748213309 /* PearlEMail.m in Sources */, DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */, + 93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (App Store).xcscheme b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (App Store).xcscheme similarity index 100% rename from MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (App Store).xcscheme rename to MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (App Store).xcscheme diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme similarity index 100% rename from MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme rename to MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme diff --git a/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist b/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist index 00eaf961..6c6d7361 100644 --- a/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist +++ b/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist @@ -84,7 +84,7 @@ Title iCloud Key - iCloud + USMCloudEnabledKey DefaultValue