2
0

iCloud toggling + internal improvements

[UPDATED]   Change TestFlight preprocessor toggles to
            TESTFLIGHT_SDK_VERSION.
[IMPROVED]  Change logic behind shared MPAppDelegate to inheritance,
            allowing for the common files to implement methods that the
            platform-specific class can override.
[IMPROVED]  Handling and checking of config changes.
[ADDED]     iCloud config toggle, allowing a user to turn on and off
            the iCloud store.
[IMPROVED]  Explanation of iCloud.
This commit is contained in:
Maarten Billemont 2012-05-11 22:45:05 +02:00
parent 6bbd183ac9
commit 82b8de5e23
18 changed files with 160 additions and 100 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit 489ac3004875581ebd0f46256ad6a45e6172b92a
Subproject commit b45e33014850ece1a42afb4ac5363aadd0ea90d6

View File

@ -6,9 +6,9 @@
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPAppDelegate.h"
#import "MPAppDelegate_Shared.h"
@interface MPAppDelegate (Key)
@interface MPAppDelegate_Shared (Key)
- (void)loadStoredKey;
- (IBAction)signOut:(id)sender;

View File

@ -7,10 +7,10 @@
//
#import "MPConfig.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate_Key.h"
#import "MPElementEntity.h"
@implementation MPAppDelegate (Key)
@implementation MPAppDelegate_Shared (Key)
static NSDictionary *keyQuery() {
@ -43,7 +43,7 @@ static NSDictionary *keyHashQuery() {
[PearlKeyChain deleteItemForQuery:keyHashQuery()];
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyForgotten object:self];
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPForgotten];
#endif
}
@ -64,7 +64,7 @@ static NSDictionary *keyHashQuery() {
// Key should not be stored in keychain. Delete it.
if ([PearlKeyChain deleteItemForQuery:keyQuery()] != errSecItemNotFound)
dbg(@"Deleted key from key chain.");
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored];
#endif
}
@ -86,13 +86,13 @@ static NSDictionary *keyHashQuery() {
if (![keyHash isEqual:tryKeyHash]) {
dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyHash, tryKeyHash);
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch];
#endif
return NO;
}
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointMPEntered];
#endif
@ -102,15 +102,17 @@ static NSDictionary *keyHashQuery() {
- (void)updateKey:(NSData *)key {
self.key = key;
if (self.key != key) {
self.key = key;
if (key)
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeySet object:self];
else
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyUnset object:self];
}
if (key)
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeySet object:self];
else
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationKeyUnset object:self];
if (key) {
self.keyHash = keyHashForKey(key);
if (self.key) {
self.keyHash = keyHashForKey(self.key);
self.keyID = [self.keyHash encodeHex];
dbg(@"Updating key ID to: %@.", self.keyID);
@ -125,14 +127,14 @@ static NSDictionary *keyHashQuery() {
dbg(@"Storing key in key chain.");
[PearlKeyChain addOrUpdateItemForQuery:keyQuery()
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
key, (__bridge id)kSecValueData,
self.key, (__bridge id)kSecValueData,
#if TARGET_OS_IPHONE
kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible,
#endif
nil]];
}
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointSetKey];
#endif
}

View File

@ -6,22 +6,14 @@
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
@interface MPAppDelegate () {
}
@interface MPAppDelegate_Shared : PearlAppDelegate
@property (strong, nonatomic) NSData *key;
@property (strong, nonatomic) NSData *keyHash;
@property (strong, nonatomic) NSString *keyID;
@end
+ (MPAppDelegate_Shared *)get;
@interface MPAppDelegate (Shared)
+ (MPAppDelegate *)get;
- (NSURL *)applicationFilesDirectory;
@end

View File

@ -8,14 +8,18 @@
#import "MPAppDelegate_Shared.h"
@implementation MPAppDelegate (Shared)
@implementation MPAppDelegate_Shared
+ (MPAppDelegate *)get {
@synthesize key;
@synthesize keyHash;
@synthesize keyID;
+ (MPAppDelegate_Shared *)get {
#if TARGET_OS_IPHONE
return (MPAppDelegate *)[UIApplication sharedApplication].delegate;
return (MPAppDelegate_Shared *)[UIApplication sharedApplication].delegate;
#elif defined (__MAC_OS_X_VERSION_MIN_REQUIRED)
return (MPAppDelegate *)[NSApplication sharedApplication].delegate;
return (MPAppDelegate_Shared *)[NSApplication sharedApplication].delegate;
#else
#error Unsupported OS.
#endif

View File

@ -18,7 +18,7 @@ typedef enum {
MPImportResultInternalError,
} MPImportResult;
@interface MPAppDelegate (Store) <UbiquityStoreManagerDelegate>
@interface MPAppDelegate_Shared (Store) <UbiquityStoreManagerDelegate>
+ (NSManagedObjectContext *)managedObjectContext;
+ (NSManagedObjectModel *)managedObjectModel;

View File

@ -9,7 +9,7 @@
#import "MPAppDelegate_Store.h"
#import "MPElementEntity.h"
@implementation MPAppDelegate (Store)
@implementation MPAppDelegate_Shared (Store)
static NSDateFormatter *rfc3339DateFormatter = nil;
@ -148,13 +148,13 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
}
}
trc(@"---");
if ([MPAppDelegate get].keyID) {
if ([MPAppDelegate_Shared get].keyID) {
trc(@"=== Known sites ===");
NSFetchRequest *fetchRequest = [[self managedObjectModel]
fetchRequestFromTemplateWithName:@"MPElements"
substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:
@"", @"query",
[MPAppDelegate get].keyID, @"keyID",
[MPAppDelegate_Shared get].keyID, @"keyID",
nil]];
[fetchRequest setSortDescriptors:
[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]];
@ -181,9 +181,19 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
dbg(@"StoreManager: %@", message);
}
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)didSwitch {
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:didSwitch? MPTestFlightCheckpointCloudEnabled: MPTestFlightCheckpointCloudDisabled];
#endif
inf(@"Using iCloud? %@", didSwitch? @"YES": @"NO");
[MPConfig get].iCloud = [NSNumber numberWithBool:didSwitch];
}
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didEncounterError:(NSError *)error cause:(UbiquityStoreManagerErrorCause)cause context:(id)context {
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:str(@"MPTestFlightCheckpointMPErrorUbiquity_%d", cause)];
#endif
err(@"StoreManager: cause=%d, context=%@, error=%@", cause, context, error);
@ -195,7 +205,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
case UbiquityStoreManagerErrorCauseClearStore:
break;
case UbiquityStoreManagerErrorCauseOpenLocalStore: {
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointLocalStoreIncompatible];
#endif
wrn(@"Local store could not be opened, resetting it.");
@ -206,7 +216,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
return;
}
case UbiquityStoreManagerErrorCauseOpenCloudStore: {
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointCloudStoreIncompatible];
#endif
wrn(@"iCloud store could not be opened, resetting it.");
@ -355,7 +365,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
}
[self saveContext];
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointSitesImported];
#endif
@ -415,7 +425,7 @@ static NSDateFormatter *rfc3339DateFormatter = nil;
[rfc3339DateFormatter stringFromDate:[NSDate dateWithTimeIntervalSinceReferenceDate:lastUsed]], uses, type, [name cStringUsingEncoding:NSUTF8StringEncoding], content? content: @""];
}
#if TARGET_OS_IPHONE
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:MPTestFlightCheckpointSitesExported];
#endif

View File

@ -11,6 +11,9 @@
@property (nonatomic, retain) NSNumber *saveKey;
@property (nonatomic, retain) NSNumber *rememberKey;
@property (nonatomic, retain) NSNumber *iCloud;
@property (nonatomic, retain) NSNumber *iCloudDecided;
+ (MPConfig *)get;
@end

View File

@ -7,10 +7,10 @@
//
#import "MPConfig.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate.h"
@implementation MPConfig
@dynamic saveKey, rememberKey;
@dynamic saveKey, rememberKey, iCloud, iCloudDecided;
- (id)init {
@ -20,6 +20,8 @@
[self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(saveKey)),
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKey)),
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(iCloud)),
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(iCloudDecided)),
nil]];
self.delegate = [MPAppDelegate get];

View File

@ -7,7 +7,8 @@
//
#import "MPElementGeneratedEntity.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
@implementation MPElementGeneratedEntity

View File

@ -7,7 +7,8 @@
//
#import "MPElementStoredEntity.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
@interface MPElementStoredEntity ()

View File

@ -8,8 +8,11 @@
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#import "MPAppDelegate_Shared.h"
@interface MPAppDelegate : PearlAppDelegate <MFMailComposeViewControllerDelegate>
@interface MPAppDelegate : MPAppDelegate_Shared <MFMailComposeViewControllerDelegate>
+ (MPAppDelegate *)get;
- (void)showGuide;
- (void)loadKey:(BOOL)animated;

View File

@ -6,7 +6,9 @@
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
#import "MPMainViewController.h"
#import "IASKSettingsReader.h"
@ -30,10 +32,6 @@
@implementation MPAppDelegate
@synthesize key;
@synthesize keyHash;
@synthesize keyID;
+ (void)initialize {
[MPiOSConfig get];
@ -43,6 +41,12 @@
//[NSClassFromString(@"WebView") performSelector:NSSelectorFromString(@"_enableRemoteInspector")];
#endif
}
+ (MPAppDelegate *)get {
return (MPAppDelegate *)[super get];
}
- (void)showGuide {
[self.navigationController performSegueWithIdentifier:@"MP_Guide" sender:self];
@ -229,14 +233,7 @@
[[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil
usingBlock:^(NSNotification *note) {
if ([NSStringFromSelector(@selector(saveKey))
isEqualToString:[note.object description]]) {
[self updateKey:self.key];
[self loadKey:YES];
}
if ([NSStringFromSelector(@selector(forgetKey))
isEqualToString:[note.object description]])
[self loadKey:YES];
[self checkConfig];
}];
#ifdef ADHOC
@ -324,8 +321,10 @@
if ([[MPiOSConfig get].showQuickStart boolValue])
[self showGuide];
else
else {
[self loadKey:NO];
[self checkConfig];
}
[TestFlight passCheckpoint:MPTestFlightCheckpointActivated];
}
@ -368,6 +367,18 @@
[TestFlight passCheckpoint:MPTestFlightCheckpointDeactivated];
}
- (void)checkConfig {
if ([[MPConfig get].saveKey boolValue]) {
if (self.key)
[self updateKey:self.key];
} else
[self loadStoredKey];
if ([[MPConfig get].iCloud boolValue] != [self.storeManager iCloudEnabled])
[self.storeManager useiCloudStore:[[MPConfig get].iCloud boolValue] alertUser:YES];
}
#pragma mark - MFMailComposeViewControllerDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller
@ -396,39 +407,54 @@
#pragma mark - UbiquityStoreManagerDelegate
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)didSwitch {
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)iCloudEnabled {
#if TARGET_OS_IPHONE
[TestFlight passCheckpoint:didSwitch? MPTestFlightCheckpointCloudEnabled: MPTestFlightCheckpointCloudDisabled];
#endif
[super ubiquityStoreManager:manager didSwitchToiCloud:iCloudEnabled];
if (![[MPConfig get].iCloudDecided boolValue]) {
if (!iCloudEnabled) {
[PearlAlert showAlertWithTitle:@"iCloud"
message:
@"iCloud is now disabled.\n\n"
@"It is highly recommended you enable iCloud."
viewStyle:UIAlertViewStyleDefault tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
[MPConfig get].iCloudDecided = [NSNumber numberWithBool:YES];
inf(@"Using iCloud? %@", didSwitch? @"YES": @"NO");
if (!didSwitch) {
[PearlAlert showAlertWithTitle:@"iCloud"
message:
@"iCloud is now disabled.\n"
@"It is highly recommended you enable iCloud. "
@"Doing so will let you easily access all your sites from any of your devices. "
@"It will also make it easier to recover from the loss of a device.\n\n"
@"iCloud only backs up your site names. If you use stored passwords, "
@"those are always encrypted with your master password. "
@"Apple cannot see any of your private information."
viewStyle:UIAlertViewStyleDefault tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[manager useiCloudStore:YES alertUser:YES];
} cancelTitle:@"Leave Off" otherTitles:@"Enable iCloud", nil];
if (buttonIndex == [alert cancelButtonIndex])
return;
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
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
[self ubiquityStoreManager:manager didSwitchToiCloud:iCloudEnabled];
}
cancelTitle:[PearlStrings get].commonButtonThanks otherTitles:nil];
if (buttonIndex == [alert firstOtherButtonIndex] + 1)
[manager useiCloudStore:YES alertUser:NO];
} cancelTitle:@"Leave iCloud Off" otherTitles:@"Explain?", @"Enable iCloud", nil];
}
}
}
#pragma mark - TestFlight
static NSDictionary *testFlightInfo = nil;
- (NSDictionary *)testFlightInfo {
static NSDictionary *testFlightInfo = nil;
if (testFlightInfo == nil)
testFlightInfo = [[NSDictionary alloc] initWithContentsOfURL:
[[NSBundle mainBundle] URLForResource:@"TestFlight" withExtension:@"plist"]];
@ -445,10 +471,9 @@ static NSDictionary *testFlightInfo = nil;
#pragma mark - Crashlytics
static NSDictionary *crashlyticsInfo = nil;
- (NSDictionary *)crashlyticsInfo {
static NSDictionary *crashlyticsInfo = nil;
if (crashlyticsInfo == nil)
crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
[[NSBundle mainBundle] URLForResource:@"Crashlytics" withExtension:@"plist"]];
@ -465,10 +490,9 @@ static NSDictionary *crashlyticsInfo = nil;
#pragma mark - Localytics
static NSDictionary *localyticsInfo = nil;
- (NSDictionary *)localyticsInfo {
static NSDictionary *localyticsInfo = nil;
if (localyticsInfo == nil)
localyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
[[NSBundle mainBundle] URLForResource:@"Localytics" withExtension:@"plist"]];

View File

@ -7,7 +7,8 @@
//
#import "MPGuideViewController.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate.h"
@implementation MPGuideViewController
@synthesize scrollView;

View File

@ -7,7 +7,9 @@
//
#import "MPMainViewController.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
#import "MPElementGeneratedEntity.h"
#import "MPElementStoredEntity.h"
#import "IASKAppSettingsViewController.h"
@ -396,10 +398,11 @@
case 0:
[self toggleHelpAnimated:YES];
break;
case 1:
case 1: {
[self setHelpChapter:@"faq"];
[self setHelpHidden:NO animated:YES];
break;
}
case 2:
[[MPAppDelegate get] showGuide];
break;
@ -426,14 +429,8 @@
break;
#ifdef ADHOC
case 7:
[[MPAppDelegate get].storeManager useiCloudStore:![MPAppDelegate get].storeManager.iCloudEnabled alertUser:YES];
break;
case 8:
#else
case 6:
[[MPAppDelegate get].storeManager useiCloudStore:![MPAppDelegate get].storeManager.iCloudEnabled alertUser:YES];
break;
case 7:
#endif
#endif
{
@ -447,12 +444,12 @@
} cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil
otherTitles:
[self isHelpVisible]? @"Hide Help": @"Show Help", @"FAQ", @"Tutorial", @"Settings", @"Export",
#ifdef ADHOC
@"Feedback",
#endif
#ifdef DEBUG
@"Reset iCloud",
@"Toggle iCloud",
#endif
@"Sign Out",
nil];

View File

@ -7,7 +7,8 @@
//
#import "MPSearchDelegate.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate.h"
#import "MPAppDelegate_Store.h"
#import "MPElementGeneratedEntity.h"
@interface MPSearchDelegate (Private)

View File

@ -9,7 +9,8 @@
#import <QuartzCore/QuartzCore.h>
#import "MPUnlockViewController.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
typedef enum {
MPLockscreenIdle,

View File

@ -78,6 +78,24 @@
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
</dict>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string></string>
<key>FooterText</key>
<string>Makes your sites available to all your Apple devices. This also works as a way of automatically keeping a back-up of your sites. Apple cannot see any of your passwords.</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>iCloud</string>
<key>Key</key>
<string>iCloud</string>
<key>DefaultValue</key>
<true/>
</dict>
</array>
<key>StringsTable</key>
<string>Root</string>