2
0

Core Data context fixes and migration fixes.

[FIXED]     More careful Core Data threading and context usage.
[FIXED]     iOS bug when migrating, now using USM's copy migration.
[ADDED]     Better handling of store changes while in Main VC and Element List VCs.
This commit is contained in:
Maarten Billemont 2013-04-28 00:33:28 -04:00
parent 07e44a46ae
commit d27a0fdbad
9 changed files with 147 additions and 180 deletions

@ -1 +1 @@
Subproject commit 25a30fadcb80e337a664127c6c86c4fc46e96e5f Subproject commit 9a4d52c382ae01f81cc9d94982b58222b3240f73

View File

@ -10,7 +10,7 @@
@interface MPAppDelegate_Shared(Key) @interface MPAppDelegate_Shared(Key)
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password; - (BOOL)signInAsUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password;
- (void)signOutAnimated:(BOOL)animated; - (void)signOutAnimated:(BOOL)animated;
- (void)storeSavedKeyFor:(MPUserEntity *)user; - (void)storeSavedKeyFor:(MPUserEntity *)user;

View File

@ -77,18 +77,20 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self userInfo:@{ @"animated" : @(animated) }]; [[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self userInfo:@{ @"animated" : @(animated) }];
} }
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password { - (BOOL)signInAsUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password {
if (password)
NSAssert(![NSThread isMainThread], @"Computing key may not happen from the main thread.");
assert(!password || ![NSThread isMainThread]); // If we need to computing a key, this operation shouldn't be on the main thread.
MPKey *tryKey = nil; MPKey *tryKey = nil;
// Method 1: When the user has no keyID set, set a new key from the given master password. // Method 1: When the user has no keyID set, set a new key from the given master password.
if (!user.keyID) { if (!user.keyID) {
if ([password length]) if ((tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) { if ([password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
user.keyID = tryKey.keyID; user.keyID = tryKey.keyID;
// Migrate existing elements. // Migrate existing elements.
[self migrateElementsForUser:user inContext:user.managedObjectContext toKey:tryKey]; [self migrateElementsForUser:user inContext:moc toKey:tryKey];
} }
} }
@ -153,7 +155,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
} }
user.lastUsed = [NSDate date]; user.lastUsed = [NSDate date];
[user.managedObjectContext saveToStore]; [moc saveToStore];
self.activeUser = user; self.activeUser = user;
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedInNotification object:self]; [[NSNotificationCenter defaultCenter] postNotificationName:MPSignedInNotification object:self];

View File

@ -26,19 +26,7 @@
- (MPUserEntity *)activeUserForThread { - (MPUserEntity *)activeUserForThread {
if (!_activeUserOID) return [self activeUserInContext:[MPAppDelegate_Shared managedObjectContextForThreadIfReady]];
return nil;
NSManagedObjectContext *moc = [MPAppDelegate_Shared managedObjectContextForThreadIfReady];
if (!moc)
return nil;
NSError *error;
MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:_activeUserOID error:&error];
if (!activeUser)
err(@"Failed to retrieve active user: %@", error);
return activeUser;
} }
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc { - (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc {

View File

@ -17,7 +17,7 @@
@implementation MPAppDelegate_Shared(Store) @implementation MPAppDelegate_Shared(Store)
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
PearlAssociatedObjectProperty(PearlAlert*, HandleCloudContentAlert, handleCloudContentAlert); PearlAssociatedObjectProperty(PearlAlert*, HandleCloudContentAlert, handleCloudContentAlert);
PearlAssociatedObjectProperty(PearlAlert*, FixCloudContentAlert, fixCloudContentAlert); PearlAssociatedObjectProperty(PearlAlert*, FixCloudContentAlert, fixCloudContentAlert);
PearlAssociatedObjectProperty(PearlOverlay*, StoreLoading, storeLoading); PearlAssociatedObjectProperty(PearlOverlay*, StoreLoading, storeLoading);
#endif #endif
@ -157,8 +157,6 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
NSMigratePersistentStoresAutomaticallyOption : @YES, NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES NSInferMappingModelAutomaticallyOption : @YES
}; };
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// Create the directory to hold the new local store. // Create the directory to hold the new local store.
if (![[NSFileManager defaultManager] createDirectoryAtPath:[manager URLForLocalStoreDirectory].path if (![[NSFileManager defaultManager] createDirectoryAtPath:[manager URLForLocalStoreDirectory].path
@ -168,31 +166,13 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
return; return;
} }
// Open the old local store. if (![manager copyMigrateStore:oldLocalStoreURL withOptions:oldLocalStoreOptions
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldLocalStoreURL toStore:newLocalStoreURL withOptions:newLocalStoreOptions
options:oldLocalStoreOptions error:&error]; error:nil cause:nil context:nil]) {
if (!oldStore) {
err(@"While opening old store for migration %@: %@", oldLocalStoreURL.path, error);
return;
}
// Migrate to the new local store.
if (![psc migratePersistentStore:oldStore toURL:newLocalStoreURL options:newLocalStoreOptions withType:NSSQLiteStoreType
error:&error]) {
err(@"While migrating local store from %@ -> %@: %@", oldLocalStoreURL, newLocalStoreURL, error);
manager.localStoreURL = oldLocalStoreURL; manager.localStoreURL = oldLocalStoreURL;
return; return;
} }
// Clean-up.
for (NSPersistentStore *store in psc.persistentStores)
if (![psc removePersistentStore:store error:&error]) {
err(@"While removing the migrated store from the store context: %@", error);
}
if (![[NSFileManager defaultManager] removeItemAtURL:oldLocalStoreURL error:&error]) {
err(@"While deleting the old local store: %@", error);
}
inf(@"Successfully migrated old to new local store."); inf(@"Successfully migrated old to new local store.");
} }
@ -260,32 +240,10 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
return; return;
} }
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; if (![manager copyMigrateStore:oldCloudStoreURL withOptions:oldCloudStoreOptions
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; toStore:newCloudStoreURL withOptions:newCloudStoreOptions
error:nil cause:nil context:nil])
// Open the old cloud store.
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldCloudStoreURL
options:oldCloudStoreOptions error:&error];
if (!oldStore) {
err(@"While opening old store for migration %@: %@", oldCloudStoreURL.path, error);
return; return;
}
// Migrate to the new cloud store.
if (![psc migratePersistentStore:oldStore toURL:newCloudStoreURL options:newCloudStoreOptions withType:NSSQLiteStoreType
error:&error]) {
err(@"While migrating cloud store from %@ -> %@: %@", oldCloudStoreURL.path, newCloudStoreURL.path, error);
return;
}
// Clean-up.
for (NSPersistentStore *store in psc.persistentStores)
if (![psc removePersistentStore:store error:&error]) {
err(@"While removing the migrated store from the store context: %@", error);
}
if (![[NSFileManager defaultManager] removeItemAtURL:oldCloudStoreURL error:&error]) {
err(@"While deleting the old cloud store: %@", error);
}
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"LocalUUIDKey"]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"LocalUUIDKey"];
inf(@"Successfully migrated old to new cloud store."); inf(@"Successfully migrated old to new cloud store.");
@ -322,7 +280,6 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore { - (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore {
// FIXME
self.privateManagedObjectContext = nil; self.privateManagedObjectContext = nil;
self.mainManagedObjectContext = nil; self.mainManagedObjectContext = nil;
@ -413,6 +370,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
cancelTitle:[PearlStrings get].commonButtonBack otherTitles:@"Fix Anyway", nil]; cancelTitle:[PearlStrings get].commonButtonBack otherTitles:@"Fix Anyway", nil];
}; };
} }
#endif #endif
#pragma mark - Import / Export #pragma mark - Import / Export

View File

@ -13,6 +13,16 @@
NSDateFormatter *_dateFormatter; NSDateFormatter *_dateFormatter;
} }
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
[self updateData];
}];
[super viewDidLoad];
}
- (void)addElementNamed:(NSString *)siteName completion:(void (^)(BOOL success))completion { - (void)addElementNamed:(NSString *)siteName completion:(void (^)(BOOL success))completion {
if (![siteName length]) { if (![siteName length]) {
@ -105,8 +115,12 @@
- (void)updateData { - (void)updateData {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForThread]; MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForThread];
if (!activeUser) if (!activeUser) {
_fetchedResultsControllerByLastUsed = nil;
_fetchedResultsControllerByUses = nil;
[self.tableView reloadData];
return; return;
}
// Build predicate. // Build predicate.
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@", activeUser]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@", activeUser];

View File

@ -63,11 +63,6 @@
} }
} }
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[self.delegate didSelectElement:nil];
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller { - (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
controller.searchBar.text = @""; controller.searchBar.text = @"";

View File

@ -77,10 +77,10 @@
self.alertBody.text = nil; self.alertBody.text = nil;
self.toolTipEditIcon.hidden = YES; self.toolTipEditIcon.hidden = YES;
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:self queue:nil usingBlock:
addObserverForName:UIApplicationDidEnterBackgroundNotification object:self queue:nil usingBlock:^(NSNotification *note) { ^(NSNotification *note) {
self.suppressOutdatedAlert = NO; self.suppressOutdatedAlert = NO;
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:MPElementUpdatedNotification object:nil queue:nil usingBlock: [[NSNotificationCenter defaultCenter] addObserverForName:MPElementUpdatedNotification object:nil queue:nil usingBlock:
^void(NSNotification *note) { ^void(NSNotification *note) {
MPElementEntity *activeElement = [self activeElementForThread]; MPElementEntity *activeElement = [self activeElementForThread];
@ -107,6 +107,11 @@
[self.navigationController popToRootViewControllerAnimated:animated]; [self.navigationController popToRootViewControllerAnimated:animated];
}]; }];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
if (!self.activeElementForThread)
[self didSelectElement:nil];
}];
[super viewDidLoad]; [super viewDidLoad];
} }
@ -584,37 +589,43 @@
- (void)changeActiveElementWithoutWarningDo:(BOOL (^)(MPElementEntity *activeElement))task; { - (void)changeActiveElementWithoutWarningDo:(BOOL (^)(MPElementEntity *activeElement))task; {
MPElementEntity *activeElement = [self activeElementForThread]; [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSString *oldPassword = [activeElement.content description]; MPElementEntity *activeElement = [self activeElementInContext:context];
if (!task( activeElement )) if (!activeElement)
return; return;
NSString *newPassword = [activeElement.content description];
// Save. NSString *oldPassword = [activeElement.content description];
[activeElement.managedObjectContext saveToStore]; if (!task( activeElement ))
return;
NSString *newPassword = [activeElement.content description];
// Update the UI. // Save.
dispatch_async( dispatch_get_main_queue(), ^{ [context saveToStore];
[self updateAnimated:YES];
// Show new and old password. // Update the UI.
if ([oldPassword length] && ![oldPassword isEqualToString:newPassword]) dispatch_async( dispatch_get_main_queue(), ^{
[self showAlertWithTitle:@"Password Changed!" [self updateAnimated:YES];
message:PearlString( @"The password for %@ has changed.\n\n"
@"IMPORTANT:\n" // Show new and old password.
@"Don't forget to update the site with your new password! " if ([oldPassword length] && ![oldPassword isEqualToString:newPassword])
@"Your old password was:\n" [self showAlertWithTitle:@"Password Changed!"
@"%@", activeElement.name, oldPassword )]; message:PearlString( @"The password for %@ has changed.\n\n"
} ); @"IMPORTANT:\n"
@"Don't forget to update the site with your new password! "
@"Your old password was:\n"
@"%@", activeElement.name, oldPassword )];
} );
}];
} }
- (MPElementEntity *)activeElementForThread { - (MPElementEntity *)activeElementForThread {
if (!_activeElementOID) return [self activeElementInContext:[MPiOSAppDelegate managedObjectContextForThreadIfReady]];
return nil; }
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady]; - (MPElementEntity *)activeElementInContext:(NSManagedObjectContext *)moc {
if (!moc)
if (!_activeElementOID)
return nil; return nil;
NSError *error; NSError *error;
@ -801,47 +812,45 @@
- (void)didSelectElement:(MPElementEntity *)element { - (void)didSelectElement:(MPElementEntity *)element {
if (!element) inf(@"Selected: %@", element.name);
return;
_activeElementOID = element.objectID; _activeElementOID = element.objectID;
[self closeAlert]; [self closeAlert];
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement) {
if ([activeElement use] == 1)
[self showAlertWithTitle:@"New Site" message:
PearlString( @"You've just created a password for %@.\n\n"
@"IMPORTANT:\n"
@"Go to %@ and set or change the password for your account to the password above.\n"
@"Do this right away: if you forget, you may have trouble remembering which password to use to log into the site later on.",
activeElement.name, activeElement.name )];
return YES;
}];
MPElementEntity *activeElement = [self activeElementForThread]; if (element) {
inf(@"Selected: %@", activeElement.name); [self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement) {
if ([activeElement use] == 1)
if (![[MPiOSConfig get].typeTipShown boolValue]) [self showAlertWithTitle:@"New Site" message:
[UIView animateWithDuration:0.5f animations:^{ PearlString( @"You've just created a password for %@.\n\n"
self.typeTipContainer.alpha = 1; @"IMPORTANT:\n"
} completion:^(BOOL finished) { @"Go to %@ and set or change the password for your account to the password above.\n"
if (finished) { @"Do this right away: if you forget, you may have trouble remembering which password to use to log into the site later on.",
[MPiOSConfig get].typeTipShown = PearlBool(YES); activeElement.name, activeElement.name )];
return YES;
dispatch_after(
dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.2f animations:^{
self.typeTipContainer.alpha = 0;
}];
} );
}
}]; }];
if (![[MPiOSConfig get].typeTipShown boolValue])
[UIView animateWithDuration:0.5f animations:^{
self.typeTipContainer.alpha = 1;
} completion:^(BOOL finished) {
if (finished) {
[MPiOSConfig get].typeTipShown = PearlBool(YES);
dispatch_after(
dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.2f animations:^{
self.typeTipContainer.alpha = 0;
}];
} );
}
}];
}
[self.searchDisplayController setActive:NO animated:YES]; [self.searchDisplayController setActive:NO animated:YES];
self.searchDisplayController.searchBar.text = activeElement.name; self.searchDisplayController.searchBar.text = element.name;
MPCheckpoint( MPCheckpointUseType, @{ MPCheckpoint( MPCheckpointUseType, @{
@"type" : activeElement.typeName, @"type" : element.typeName,
@"version" : @(activeElement.version) @"version" : @(element.version)
} ); } );
[self updateAnimated:YES]; [self updateAnimated:YES];

View File

@ -14,8 +14,6 @@
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "GPPSignIn.h"
@interface MPUnlockViewController() @interface MPUnlockViewController()
@property(strong, nonatomic) NSMutableDictionary *avatarToUserOID; @property(strong, nonatomic) NSMutableDictionary *avatarToUserOID;
@ -163,26 +161,26 @@
[self initializeWordLabel:wordLabel]; [self initializeWordLabel:wordLabel];
} recurse:NO]; } recurse:NO];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) { ^(NSNotification *note) {
[self updateUsers]; [self updateUsers];
}]; }];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil
addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil usingBlock:^(NSNotification *note) { usingBlock:^(NSNotification *note) {
[self updateUsers]; [self updateUsers];
}]; }];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil usingBlock:
addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) { ^(NSNotification *note) {
[self emergencyCloseAnimated:NO]; [self emergencyCloseAnimated:NO];
self.uiContainer.alpha = 0; self.uiContainer.alpha = 0;
}]; }];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification *note) { ^(NSNotification *note) {
[self updateLayoutAnimated:NO allowScroll:NO completion:nil]; [self updateLayoutAnimated:NO allowScroll:NO completion:nil];
[UIView animateWithDuration:1 animations:^{ [UIView animateWithDuration:1 animations:^{
self.uiContainer.alpha = 1; self.uiContainer.alpha = 1;
}]; }];
}]; }];
[self updateLayoutAnimated:NO allowScroll:YES completion:nil]; [self updateLayoutAnimated:NO allowScroll:YES completion:nil];
@ -356,16 +354,16 @@
- (void)didToggleUserSelection { - (void)didToggleUserSelection {
MPUserEntity *selectedUser = self.selectedUser; MPUserEntity *selectedUser = [self selectedUserForThread];
if (!selectedUser) if (!selectedUser)
[self.passwordField resignFirstResponder]; [self.passwordField resignFirstResponder];
else if ([[MPiOSAppDelegate get] signInAsUser:selectedUser usingMasterPassword:nil]) { else if ([[MPiOSAppDelegate get] signInAsUser:selectedUser inContext:selectedUser.managedObjectContext usingMasterPassword:nil]) {
[self performSegueWithIdentifier:@"MP_Unlock" sender:self]; [self performSegueWithIdentifier:@"MP_Unlock" sender:self];
return; return;
} }
[self updateLayoutAnimated:YES allowScroll:YES completion:^(BOOL finished) { [self updateLayoutAnimated:YES allowScroll:YES completion:^(BOOL finished) {
if (self.selectedUser) if ([self selectedUserForThread])
[self.passwordField becomeFirstResponder]; [self.passwordField becomeFirstResponder];
}]; }];
} }
@ -479,7 +477,8 @@
} }
// Lay out password entry and user selection views. // Lay out password entry and user selection views.
if (self.selectedUser && !self.passwordView.alpha) { MPUserEntity *selectedUser = [self selectedUserForThread];
if (selectedUser && !self.passwordView.alpha) {
// User was just selected. // User was just selected.
self.passwordView.alpha = 1; self.passwordView.alpha = 1;
self.avatarsView.center = CGPointMake( 160, 180 ); self.avatarsView.center = CGPointMake( 160, 180 );
@ -489,7 +488,7 @@
self.oldNameLabel.center = self.nameLabel.center; self.oldNameLabel.center = self.nameLabel.center;
self.avatarShadowColor = [UIColor whiteColor]; self.avatarShadowColor = [UIColor whiteColor];
} }
else if (!self.selectedUser && self.passwordView.alpha == 1) { else if (!selectedUser && self.passwordView.alpha == 1) {
// User was just deselected. // User was just deselected.
self.passwordField.text = nil; self.passwordField.text = nil;
self.passwordView.alpha = 0; self.passwordView.alpha = 0;
@ -502,7 +501,7 @@
} }
// Lay out the word wall. // Lay out the word wall.
if (!self.selectedUser || self.selectedUser.keyID) { if (!selectedUser || selectedUser.keyID) {
self.passwordFieldLabel.text = @"Enter your master password:"; self.passwordFieldLabel.text = @"Enter your master password:";
self.wordWall.alpha = 0; self.wordWall.alpha = 0;
@ -530,8 +529,8 @@
} }
// Lay out user targeting. // Lay out user targeting.
MPUserEntity *targetedUser = self.selectedUser; UIButton *selectedAvatar = [self avatarForUser:selectedUser];
UIButton *selectedAvatar = [self avatarForUser:self.selectedUser]; MPUserEntity *targetedUser = selectedUser;
UIButton *targetedAvatar = selectedAvatar; UIButton *targetedAvatar = selectedAvatar;
if (!targetedAvatar) { if (!targetedAvatar) {
targetedAvatar = [self findTargetedAvatar]; targetedAvatar = [self findTargetedAvatar];
@ -547,7 +546,7 @@
BOOL isTargeted = avatar == targetedAvatar; BOOL isTargeted = avatar == targetedAvatar;
avatar.userInteractionEnabled = isTargeted; avatar.userInteractionEnabled = isTargeted;
avatar.alpha = isTargeted? 1: self.selectedUser? 0.1: 0.4; avatar.alpha = isTargeted? 1: [self selectedUserForThread]? 0.1: 0.4;
[self updateAvatarShadowColor:avatar isTargeted:isTargeted]; [self updateAvatarShadowColor:avatar isTargeted:isTargeted];
} recurse:NO]; } recurse:NO];
@ -612,14 +611,15 @@
- (void)tryMasterPassword { - (void)tryMasterPassword {
if (!self.selectedUser) if (![self selectedUserForThread])
// No user selected, can't try sign-in. // No user selected, can't try sign-in.
return; return;
[self setSpinnerActive:YES]; [self setSpinnerActive:YES];
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{ [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
BOOL unlocked = [[MPiOSAppDelegate get] signInAsUser:self.selectedUser usingMasterPassword:self.passwordField.text]; BOOL unlocked = [[MPiOSAppDelegate get] signInAsUser:[self selectedUserInContext:moc] inContext:moc
usingMasterPassword:self.passwordField.text];
dispatch_async( dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
if (unlocked) if (unlocked)
@ -632,7 +632,7 @@
[self setSpinnerActive:NO]; [self setSpinnerActive:NO];
} }
} ); } );
} ); }];
} }
- (UIButton *)findTargetedAvatar { - (UIButton *)findTargetedAvatar {
@ -697,9 +697,9 @@
self.spinner.alpha = active? 1: 0; self.spinner.alpha = active? 1: 0;
if (active) if (active)
[self avatarForUser:self.selectedUser].backgroundColor = [UIColor clearColor]; [self avatarForUser:[self selectedUserForThread]].backgroundColor = [UIColor clearColor];
else else
[self avatarForUser:self.selectedUser].backgroundColor = self.avatarTemplate.backgroundColor; [self avatarForUser:[self selectedUserForThread]].backgroundColor = self.avatarTemplate.backgroundColor;
}]; }];
}); });
} }
@ -779,7 +779,7 @@
else if (textField == self.passwordField) { else if (textField == self.passwordField) {
[self setSpinnerActive:YES]; [self setSpinnerActive:YES];
if (self.selectedUser.keyID) if ([self selectedUserForThread].keyID)
[self tryMasterPassword]; [self tryMasterPassword];
else else
@ -980,7 +980,7 @@
if (sender.state != UIGestureRecognizerStateBegan) if (sender.state != UIGestureRecognizerStateBegan)
return; return;
if (self.selectedUser) if ([self selectedUserForThread])
return; return;
MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar]]; MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar]];
@ -1118,15 +1118,16 @@
#pragma mark - Core Data #pragma mark - Core Data
- (MPUserEntity *)selectedUser { - (MPUserEntity *)selectedUserForThread {
return [self selectedUserInContext:[MPiOSAppDelegate managedObjectContextForThreadIfReady]];
}
- (MPUserEntity *)selectedUserInContext:(NSManagedObjectContext *)moc {
if (!_selectedUserOID) if (!_selectedUserOID)
return nil; return nil;
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
return nil;
NSError *error; NSError *error;
MPUserEntity *selectedUser = (MPUserEntity *)[moc existingObjectWithID:_selectedUserOID error:&error]; MPUserEntity *selectedUser = (MPUserEntity *)[moc existingObjectWithID:_selectedUserOID error:&error];
if (!selectedUser) if (!selectedUser)