2
0

iCloud fixes.

[UPDATED]   Moved shared MPAppDelegate code into separate files
            depending on the code's responsibility.
[UPDATED]   iCloud implementation removed in favor of
            iCloudStoreManager's managed implementation.
[FIXED]     iCloud configuration made AppStore friendly.
This commit is contained in:
Maarten Billemont
2012-05-07 22:18:01 +02:00
parent 98080ceb51
commit f622b2c7d4
22 changed files with 560 additions and 270 deletions

View File

@@ -8,34 +8,8 @@
#import "MPAppDelegate.h"
@interface MPAppDelegate () {
NSPersistentStoreCoordinator *_persistentStoreCoordinator;
NSManagedObjectModel *_managedObjectModel;
NSManagedObjectContext *_managedObjectContext;
}
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSData *key;
@property (strong, nonatomic) NSData *keyHash;
@property (strong, nonatomic) NSString *keyHashHex;
@end
@interface MPAppDelegate (Key)
+ (MPAppDelegate *)get;
- (NSURL *)applicationFilesDirectory;
+ (NSManagedObjectModel *)managedObjectModel;
+ (NSManagedObjectContext *)managedObjectContext;
- (void)saveContext;
- (void)printStore;
- (void)loadStoredKey;
- (IBAction)signOut:(id)sender;

View File

@@ -7,7 +7,7 @@
//
#import "MPConfig.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Shared.h"
#import "MPElementEntity.h"
@implementation MPAppDelegate (Key)
@@ -36,181 +36,6 @@ static NSDictionary *keyHashQuery() {
return MPKeyHashQuery;
}
- (NSURL *)applicationFilesDirectory {
#if __IPHONE_OS_VERSION_MIN_REQUIRED
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
#else
NSURL *appSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *applicationFilesDirectory = [appSupportURL URLByAppendingPathComponent:@"com.lyndir.lhunath.MasterPassword"];
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtURL:applicationFilesDirectory withIntermediateDirectories:YES attributes:nil error:&error];
if (error)
err(@"Couldn't create application directory: %@, error occurred: %@", applicationFilesDirectory, error);
return applicationFilesDirectory;
#endif
}
#pragma mark - Core Data stack
+ (NSManagedObjectContext *)managedObjectContext {
return [[self get] managedObjectContext];
}
+ (NSManagedObjectModel *)managedObjectModel {
return [[self get] managedObjectModel];
}
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel)
return _managedObjectModel;
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MasterPassword" withExtension:@"momd"];
return _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
}
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext)
return _managedObjectContext;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator) {
// Put concurrency type on main queue, because otherwise updates break updating the search table UI.
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType/*NSPrivateQueueConcurrencyType*/];
_managedObjectContext.persistentStoreCoordinator = coordinator;
[[NSNotificationCenter defaultCenter] addObserverForName:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:coordinator
queue:nil
usingBlock:^(NSNotification *note) {
dbg(@"Ubiquitous content change: %@", note);
[_managedObjectContext performBlock:^{
[_managedObjectContext mergeChangesFromContextDidSaveNotification:note];
[self printStore];
[[NSNotificationCenter defaultCenter] postNotification:
[NSNotification notificationWithName:MPNotificationStoreUpdated
object:self userInfo:[note userInfo]]];
}];
}];
}
return _managedObjectContext;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator)
return _persistentStoreCoordinator;
NSString *contentName = @"store";
NSURL *storeURL = [[self applicationFilesDirectory] URLByAppendingPathComponent:@"MasterPassword.sqlite"];
NSURL *contentURL = [[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]
URLByAppendingPathComponent:@"logs" isDirectory:YES];
//#if DEBUG
// dbg(@"Deleting store and content.");
// NSError *storeRemovalError = nil, *contentRemovalError = nil;
// [[NSFileManager defaultManager] removeItemAtURL:storeURL error:&storeRemovalError];
// if (storeRemovalError)
// err(@"Store removal error: %@", storeRemovalError);
// else
// [[NSFileManager defaultManager] removeItemAtURL:contentURL error:&contentRemovalError];
// if (contentRemovalError)
// err(@"Content removal error: %@", contentRemovalError);
//#endif
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
[_persistentStoreCoordinator lock];
@try {
NSError *error = nil;
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL
options:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
#if __IPHONE_OS_VERSION_MIN_REQUIRED
NSFileProtectionComplete, NSPersistentStoreFileProtectionKey,
#endif
contentURL, NSPersistentStoreUbiquitousContentURLKey,
contentName, NSPersistentStoreUbiquitousContentNameKey,
nil]
error:&error]) {
ftl(@"Unresolved error %@, %@", error, [error userInfo]);
#if DEBUG
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
wrn(@"Deleted datastore: %@", storeURL);
#endif
@throw error;
}
}
@finally {
[_persistentStoreCoordinator unlock];
}
return _persistentStoreCoordinator;
}
- (void)saveContext {
[self.managedObjectContext performBlock:^{
NSError *error = nil;
if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&error])
err(@"Unresolved error %@", error);
}];
}
- (void)printStore {
if (!_managedObjectModel || !_managedObjectContext) {
trc(@"Not printing store: store not initialized.");
return;
}
[self.managedObjectContext performBlock:^{
trc(@"=== All entities ===");
for(NSEntityDescription *entity in [_managedObjectModel entities]) {
NSFetchRequest *request = [NSFetchRequest new];
[request setEntity:entity];
NSError *error;
NSArray *results = [_managedObjectContext executeFetchRequest:request error:&error];
for(NSManagedObject *o in results) {
if ([o isKindOfClass:[MPElementEntity class]]) {
MPElementEntity *e = (MPElementEntity *)o;
trc(@"For descriptor: %@, found: %@: %@ (%@)", entity.name, [o class], e.name, e.mpHashHex);
} else {
trc(@"For descriptor: %@, found: %@", entity.name, [o class]);
}
}
}
trc(@"---");
if ([MPAppDelegate get].keyHashHex) {
trc(@"=== Known sites ===");
NSFetchRequest *fetchRequest = [_managedObjectModel
fetchRequestFromTemplateWithName:@"MPElements"
substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:
@"", @"query",
[MPAppDelegate get].keyHashHex, @"mpHashHex",
nil]];
[fetchRequest setSortDescriptors:
[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]];
NSError *error = nil;
for (MPElementEntity *e in [_managedObjectContext executeFetchRequest:fetchRequest error:&error]) {
trc(@"Found site: %@ (%@): %@", e.name, e.mpHashHex, e);
}
trc(@"---");
} else
trc(@"Not printing sites: master password not set.");
}];
}
- (void)forgetKey {
dbg(@"Deleting master key and hash from key chain.");
@@ -245,17 +70,6 @@ static NSDictionary *keyHashQuery() {
}
}
+ (MPAppDelegate *)get {
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
return (MPAppDelegate *)[UIApplication sharedApplication].delegate;
#elif defined (__MAC_OS_X_VERSION_MIN_REQUIRED)
return (MPAppDelegate *)[NSApplication sharedApplication].delegate;
#else
#error Unsupported OS.
#endif
}
- (BOOL)tryMasterPassword:(NSString *)tryPassword {
NSData *keyHash = [PearlKeyChain dataOfItemForQuery:keyHashQuery()];

View File

@@ -0,0 +1,27 @@
//
// MPAppDelegate_Shared.h
// MasterPassword
//
// Created by Maarten Billemont on 24/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
@interface MPAppDelegate () {
}
@property (strong, nonatomic) NSData *key;
@property (strong, nonatomic) NSData *keyHash;
@property (strong, nonatomic) NSString *keyHashHex;
@end
@interface MPAppDelegate (Shared)
+ (MPAppDelegate *)get;
- (NSURL *)applicationFilesDirectory;
@end

View File

@@ -0,0 +1,41 @@
//
// MPAppDelegate.m
// MasterPassword
//
// Created by Maarten Billemont on 24/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPAppDelegate_Shared.h"
@implementation MPAppDelegate (Shared)
+ (MPAppDelegate *)get {
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
return (MPAppDelegate *)[UIApplication sharedApplication].delegate;
#elif defined (__MAC_OS_X_VERSION_MIN_REQUIRED)
return (MPAppDelegate *)[NSApplication sharedApplication].delegate;
#else
#error Unsupported OS.
#endif
}
- (NSURL *)applicationFilesDirectory {
#if __IPHONE_OS_VERSION_MIN_REQUIRED
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
#else
NSURL *appSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *applicationFilesDirectory = [appSupportURL URLByAppendingPathComponent:@"com.lyndir.lhunath.MasterPassword"];
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtURL:applicationFilesDirectory withIntermediateDirectories:YES attributes:nil error:&error];
if (error)
err(@"Couldn't create application directory: %@, error occurred: %@", applicationFilesDirectory, error);
return applicationFilesDirectory;
#endif
}
@end

View File

@@ -0,0 +1,24 @@
//
// MPAppDelegate_Key.h
// MasterPassword
//
// Created by Maarten Billemont on 24/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPAppDelegate_Shared.h"
#import "UbiquityStoreManager.h"
@interface MPAppDelegate (Store) <UbiquityStoreManagerDelegate>
+ (NSManagedObjectContext *)managedObjectContext;
+ (NSManagedObjectModel *)managedObjectModel;
- (NSManagedObjectContext *)managedObjectContext;
- (NSManagedObjectModel *)managedObjectModel;
- (UbiquityStoreManager *)storeManager;
- (void)saveContext;
- (void)printStore;
@end

View File

@@ -0,0 +1,173 @@
//
// MPAppDelegate.m
// MasterPassword
//
// Created by Maarten Billemont on 24/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPAppDelegate_Store.h"
#import "MPElementEntity.h"
@implementation MPAppDelegate (Store)
+ (NSManagedObjectContext *)managedObjectContext {
return [[self get] managedObjectContext];
}
+ (NSManagedObjectModel *)managedObjectModel {
return [[self get] managedObjectModel];
}
- (NSManagedObjectModel *)managedObjectModel {
static NSManagedObjectModel *managedObjectModel = nil;
if (managedObjectModel)
return managedObjectModel;
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MasterPassword" withExtension:@"momd"];
return managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
}
- (NSManagedObjectContext *)managedObjectContext {
static NSManagedObjectContext *managedObjectContext = nil;
if (managedObjectContext)
return managedObjectContext;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
assert(coordinator);
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[managedObjectContext performBlockAndWait:^{
managedObjectContext.persistentStoreCoordinator = coordinator;
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
}];
return managedObjectContext;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// Wait until the storeManager is ready.
for(__block BOOL isReady = [self storeManager].isReady; !isReady;)
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
isReady = [self storeManager].isReady;
});
assert([self storeManager].isReady);
return [self storeManager].persistentStoreCoordinator;
}
- (UbiquityStoreManager *)storeManager {
static UbiquityStoreManager *storeManager = nil;
if (storeManager)
return storeManager;
storeManager = [[UbiquityStoreManager alloc] initWithManagedObjectModel:[self managedObjectModel]
localStoreURL:[[self applicationFilesDirectory] URLByAppendingPathComponent:@"MasterPassword.sqlite"]
containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
#if TARGET_OS_IPHONE
additionalStoreOptions:[NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSPersistentStoreFileProtectionKey]
#else
additionalStoreOptions:nil
#endif
];
storeManager.delegate = self;
#ifdef DEBUG
storeManager.hardResetEnabled = YES;
#endif
#if TARGET_OS_IPHONE
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification
object:[UIApplication sharedApplication] queue:nil
#else
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillBecomeActiveNotification
object:[NSApplication sharedApplication] queue:nil
#endif
usingBlock:^(NSNotification *note) {
[storeManager checkiCloudStatus];
}];
#if TARGET_OS_IPHONE
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification
object:[UIApplication sharedApplication] queue:nil
#else
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillTerminateNotification
object:[NSApplication sharedApplication] queue:nil
#endif
usingBlock:^(NSNotification *note) {
[self saveContext];
}];
return storeManager;
}
- (NSManagedObjectContext *)managedObjectContextForUbiquityStoreManager:(UbiquityStoreManager *)usm {
return self.managedObjectContext;
}
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didSwitchToiCloud:(BOOL)didSwitch {
dbg(@"didSwitchToiCloud:%@", [NSNumber numberWithBool:didSwitch]);
//[masterViewController.iCloudSwitch setOn:didSwitch animated:YES];
}
- (void)saveContext {
[self.managedObjectContext performBlock:^{
NSError *error = nil;
if ([self.managedObjectContext hasChanges])
if (![self.managedObjectContext save:&error])
err(@"Unresolved error %@", error);
}];
}
- (void)printStore {
if (![self managedObjectModel] || ![self managedObjectContext]) {
trc(@"Not printing store: store not initialized.");
return;
}
[self.managedObjectContext performBlock:^{
trc(@"=== All entities ===");
for(NSEntityDescription *entity in [[self managedObjectModel] entities]) {
NSFetchRequest *request = [NSFetchRequest new];
[request setEntity:entity];
NSError *error;
NSArray *results = [[self managedObjectContext] executeFetchRequest:request error:&error];
for(NSManagedObject *o in results) {
if ([o isKindOfClass:[MPElementEntity class]]) {
MPElementEntity *e = (MPElementEntity *)o;
trc(@"For descriptor: %@, found: %@: %@ (%@)", entity.name, [o class], e.name, e.mpHashHex);
} else {
trc(@"For descriptor: %@, found: %@", entity.name, [o class]);
}
}
}
trc(@"---");
if ([MPAppDelegate get].keyHashHex) {
trc(@"=== Known sites ===");
NSFetchRequest *fetchRequest = [[self managedObjectModel]
fetchRequestFromTemplateWithName:@"MPElements"
substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:
@"", @"query",
[MPAppDelegate get].keyHashHex, @"mpHashHex",
nil]];
[fetchRequest setSortDescriptors:
[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]];
NSError *error = nil;
for (MPElementEntity *e in [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error]) {
trc(@"Found site: %@ (%@): %@", e.name, e.mpHashHex, e);
}
trc(@"---");
} else
trc(@"Not printing sites: master password not set.");
}];
}
@end

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Shared.h"
#import "MPConfig.h"
#import "MPElementEntity.h"
#import <Carbon/Carbon.h>
@@ -26,7 +26,6 @@
@synthesize statusMenu;
@synthesize passwordWindow;
@dynamic persistentStoreCoordinator, managedObjectModel, managedObjectContext;
@synthesize key;
@synthesize keyHash;
@synthesize keyHashHex;
@@ -90,6 +89,8 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
status = RegisterEventHotKey(35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef);
if(status != noErr)
err(@"Error registering hotkey: %d", status);
[[self storeManager] useiCloudStore:YES];
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
@@ -168,7 +169,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
{
// Save changes in the application's managed object context before the application terminates.
if (!_managedObjectContext) {
if (![self managedObjectContext]) {
return NSTerminateNow;
}

View File

@@ -7,7 +7,7 @@
//
#import "MPPasswordWindowController.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Shared.h"
#import "MPElementEntity.h"
#import "MPElementGeneratedEntity.h"

View File

@@ -4,8 +4,11 @@
<dict>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.Mac</string>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared</string>
<key>com.apple.security.app-sandbox</key>
<true/>
</dict>

View File

@@ -6,8 +6,7 @@
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Shared.h"
#import "MPMainViewController.h"
#import "IASKSettingsReader.h"
@@ -31,10 +30,6 @@
@implementation MPAppDelegate
@synthesize managedObjectModel = __managedObjectModel;
@synthesize managedObjectContext = __managedObjectContext;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
@synthesize key;
@synthesize keyHash;
@synthesize keyHashHex;

View File

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

View File

@@ -7,7 +7,7 @@
//
#import "MPMainViewController.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Shared.h"
#import "MPElementGeneratedEntity.h"
#import "MPElementStoredEntity.h"
#import "IASKAppSettingsViewController.h"
@@ -411,6 +411,22 @@
case 5:
#else
case 4:
#endif
#ifdef DEBUG
{
[[MPAppDelegate get].storeManager hardResetCloudStorage];
}
#ifdef ADHOC
case 6: {
[[MPAppDelegate get].storeManager useiCloudStore:![MPAppDelegate get].storeManager.iCloudEnabled];
}
case 7:
#else
case 5: {
[[MPAppDelegate get].storeManager useiCloudStore:![MPAppDelegate get].storeManager.iCloudEnabled];
}
case 6:
#endif
#endif
{
[[MPAppDelegate get] signOut:self];
@@ -425,6 +441,10 @@
[self isHelpVisible]? @"Hide Help": @"Show Help", @"FAQ", @"Tutorial", @"Settings",
#ifdef ADHOC
@"Feedback",
#endif
#ifdef DEBUG
@"Reset iCloud",
@"Toggle iCloud",
#endif
@"Sign Out",
nil];

View File

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

View File

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

View File

@@ -5,7 +5,10 @@
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword.shared</string>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>