Updated for new UbiquityStoreManager API.
[UPDATED] New and improved UbiquityStoreManager with better API.
This commit is contained in:
parent
2f17639eeb
commit
5e40258f54
2
External/Pearl
vendored
2
External/Pearl
vendored
@ -1 +1 @@
|
||||
Subproject commit 5d814a47ed07355077a7d292d5f8459625f137b3
|
||||
Subproject commit 5b51c3360c5e173390678899f99ab89c903465de
|
2
External/UbiquityStoreManager
vendored
2
External/UbiquityStoreManager
vendored
@ -1 +1 @@
|
||||
Subproject commit d399ae61e8610ed2f7ac93a5104517729cc6f981
|
||||
Subproject commit 22dabd3ad6f3f04bc747aebffd46a57c7f28aabd
|
@ -144,7 +144,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
}
|
||||
[user saveContext];
|
||||
#ifdef PEARL_UIKIT
|
||||
[activityAlert dismissAlert];
|
||||
[activityAlert cancelAlert];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -9,6 +9,14 @@
|
||||
#import <objc/runtime.h>
|
||||
#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
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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"]);
|
||||
}
|
||||
|
||||
|
@ -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 = "<group>"; };
|
||||
93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = "<group>"; };
|
||||
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
|
||||
93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
|
||||
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
|
||||
93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; };
|
||||
@ -3863,6 +3867,8 @@
|
||||
DAFE462515039823003ABA7C /* Resources */,
|
||||
DAFE4A1115039824003ABA7C /* UIImage+PearlScaling.h */,
|
||||
DAFE4A1215039824003ABA7C /* UIImage+PearlScaling.m */,
|
||||
93D390FADEB325D8D54A957D /* PearlOverlay.m */,
|
||||
93D3942A356B639724157982 /* PearlOverlay.h */,
|
||||
);
|
||||
path = "Pearl-UIKit";
|
||||
sourceTree = "<group>";
|
||||
@ -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;
|
||||
};
|
||||
|
@ -84,7 +84,7 @@
|
||||
<key>Title</key>
|
||||
<string>iCloud</string>
|
||||
<key>Key</key>
|
||||
<string>iCloud</string>
|
||||
<string>USMCloudEnabledKey</string>
|
||||
<key>DefaultValue</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
Loading…
Reference in New Issue
Block a user