Don't block the MOC lookup.
[IMPROVED] Don't block when MOC is not yet ready, just return nil. [IMPROVED] Outdated tip links to info, icon links to site search. [IMPROVED] Minor improvements to error handling during import.
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -28,3 +28,6 @@ Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
 | 
			
		||||
# IPA
 | 
			
		||||
/sendipa/*
 | 
			
		||||
!/sendipa/sendipa.conf
 | 
			
		||||
 | 
			
		||||
# Java
 | 
			
		||||
MasterPassword/Java/**/target
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								External/iCloudStoreManager
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								External/iCloudStoreManager
									
									
									
									
										vendored
									
									
								
							 Submodule External/iCloudStoreManager updated: 1614e65222...a7b028ff80
									
								
							@@ -20,9 +20,9 @@ typedef enum {
 | 
			
		||||
 | 
			
		||||
@interface MPAppDelegate_Shared (Store)<UbiquityStoreManagerDelegate>
 | 
			
		||||
 | 
			
		||||
+ (NSManagedObjectContext *)managedObjectContext;
 | 
			
		||||
+ (NSManagedObjectContext *)managedObjectContextIfReady;
 | 
			
		||||
+ (NSManagedObjectModel *)managedObjectModel;
 | 
			
		||||
- (NSManagedObjectContext *)managedObjectContext;
 | 
			
		||||
- (NSManagedObjectContext *)managedObjectContextIfReady;
 | 
			
		||||
- (NSManagedObjectModel *)managedObjectModel;
 | 
			
		||||
 | 
			
		||||
- (UbiquityStoreManager *)storeManager;
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma mark - Core Data setup
 | 
			
		||||
 | 
			
		||||
+ (NSManagedObjectContext *)managedObjectContext {
 | 
			
		||||
+ (NSManagedObjectContext *)managedObjectContextIfReady {
 | 
			
		||||
 | 
			
		||||
    return [[self get] managedObjectContext];
 | 
			
		||||
    return [[self get] managedObjectContextIfReady];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
+ (NSManagedObjectModel *)managedObjectModel {
 | 
			
		||||
@@ -33,7 +33,10 @@
 | 
			
		||||
    return managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (NSManagedObjectContext *)managedObjectContext {
 | 
			
		||||
- (NSManagedObjectContext *)managedObjectContextIfReady {
 | 
			
		||||
 | 
			
		||||
    if (![self storeManager].isReady)
 | 
			
		||||
        return nil;
 | 
			
		||||
 | 
			
		||||
    static NSManagedObjectContext *managedObjectContext = nil;
 | 
			
		||||
    if (managedObjectContext)
 | 
			
		||||
@@ -41,25 +44,14 @@
 | 
			
		||||
 | 
			
		||||
    managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
 | 
			
		||||
    [managedObjectContext performBlockAndWait:^{
 | 
			
		||||
        managedObjectContext.persistentStoreCoordinator = [self persistentStoreCoordinator];
 | 
			
		||||
        managedObjectContext.mergePolicy                = NSMergeByPropertyObjectTrumpMergePolicy;
 | 
			
		||||
        managedObjectContext.persistentStoreCoordinator = [self storeManager].persistentStoreCoordinator;
 | 
			
		||||
        managedObjectContext.undoManager                = [NSUndoManager new];
 | 
			
		||||
    }];
 | 
			
		||||
 | 
			
		||||
    return managedObjectContext;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
 | 
			
		||||
 | 
			
		||||
    // Start loading the store.
 | 
			
		||||
    [self storeManager];
 | 
			
		||||
 | 
			
		||||
    // Wait until the storeManager is ready.
 | 
			
		||||
    while (![self storeManager].isReady)
 | 
			
		||||
        [NSThread sleepForTimeInterval:0.1];
 | 
			
		||||
 | 
			
		||||
    return [self storeManager].persistentStoreCoordinator;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (UbiquityStoreManager *)storeManager {
 | 
			
		||||
 | 
			
		||||
    static UbiquityStoreManager *storeManager = nil;
 | 
			
		||||
@@ -112,10 +104,10 @@
 | 
			
		||||
 | 
			
		||||
- (void)saveContext {
 | 
			
		||||
 | 
			
		||||
    [self.managedObjectContext performBlock:^{
 | 
			
		||||
    [self.managedObjectContextIfReady performBlock:^{
 | 
			
		||||
        NSError *error = nil;
 | 
			
		||||
        if ([self.managedObjectContext hasChanges])
 | 
			
		||||
            if (![self.managedObjectContext save:&error])
 | 
			
		||||
        if ([self.managedObjectContextIfReady hasChanges])
 | 
			
		||||
            if (![self.managedObjectContextIfReady save:&error])
 | 
			
		||||
            err(@"While saving context: %@", error);
 | 
			
		||||
    }];
 | 
			
		||||
}
 | 
			
		||||
@@ -124,7 +116,7 @@
 | 
			
		||||
 | 
			
		||||
- (NSManagedObjectContext *)managedObjectContextForUbiquityStoreManager:(UbiquityStoreManager *)usm {
 | 
			
		||||
 | 
			
		||||
    return self.managedObjectContext;
 | 
			
		||||
    return self.managedObjectContextIfReady;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager log:(NSString *)message {
 | 
			
		||||
@@ -197,18 +189,16 @@
 | 
			
		||||
    inf(@"Importing sites.");
 | 
			
		||||
 | 
			
		||||
    static NSRegularExpression *headerPattern, *sitePattern;
 | 
			
		||||
    __autoreleasing __block NSError *error;
 | 
			
		||||
    __block NSError *error = nil;
 | 
			
		||||
    if (!headerPattern) {
 | 
			
		||||
        headerPattern = [[NSRegularExpression alloc]
 | 
			
		||||
                                              initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
 | 
			
		||||
                                                      options:0 error:&error];
 | 
			
		||||
        headerPattern = [[NSRegularExpression alloc] initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
 | 
			
		||||
                                                             options:0 error:&error];
 | 
			
		||||
        if (error)
 | 
			
		||||
        err(@"Error loading the header pattern: %@", error);
 | 
			
		||||
    }
 | 
			
		||||
    if (!sitePattern) {
 | 
			
		||||
        sitePattern = [[NSRegularExpression alloc]
 | 
			
		||||
                                            initWithPattern:@"^([^[:space:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)(:[[:digit:]]+)?[[:space:]]+([^\t]+)\t(.*)"
 | 
			
		||||
                                                    options:0 error:&error];
 | 
			
		||||
        sitePattern = [[NSRegularExpression alloc] initWithPattern:@"^([^[:space:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)(:[[:digit:]]+)?[[:space:]]+([^\t]+)\t(.*)"
 | 
			
		||||
                                                           options:0 error:&error];
 | 
			
		||||
        if (error)
 | 
			
		||||
        err(@"Error loading the site pattern: %@", error);
 | 
			
		||||
    }
 | 
			
		||||
@@ -252,9 +242,20 @@
 | 
			
		||||
 | 
			
		||||
                NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])];
 | 
			
		||||
                userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", userName];
 | 
			
		||||
                [self.managedObjectContext performBlockAndWait:^{
 | 
			
		||||
                    user = [[self.managedObjectContext executeFetchRequest:userFetchRequest error:&error] lastObject];
 | 
			
		||||
                __block NSArray *users = nil;
 | 
			
		||||
                [self.managedObjectContextIfReady performBlockAndWait:^{
 | 
			
		||||
                    users = [self.managedObjectContextIfReady executeFetchRequest:userFetchRequest error:&error];
 | 
			
		||||
                }];
 | 
			
		||||
                if (!users) {
 | 
			
		||||
                    err(@"While looking for user: %@, error: %@", userName, error);
 | 
			
		||||
                    return MPImportResultInternalError;
 | 
			
		||||
                }
 | 
			
		||||
                if ([users count] > 1) {
 | 
			
		||||
                    err(@"While looking for user: %@, found more than one: %u", userName, [users count]);
 | 
			
		||||
                    return MPImportResultInternalError;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                user = [users count]? [users lastObject]: nil;
 | 
			
		||||
                dbg(@"Found user: %@", [user debugDescription]);
 | 
			
		||||
            }
 | 
			
		||||
            if ([headerName isEqualToString:@"Key ID"])
 | 
			
		||||
@@ -298,8 +299,8 @@
 | 
			
		||||
        if (user) {
 | 
			
		||||
            fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", name, user];
 | 
			
		||||
            __block NSArray *existingSites = nil;
 | 
			
		||||
            [self.managedObjectContext performBlockAndWait:^{
 | 
			
		||||
                existingSites = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
 | 
			
		||||
            [self.managedObjectContextIfReady performBlockAndWait:^{
 | 
			
		||||
                existingSites = [self.managedObjectContextIfReady executeFetchRequest:fetchRequest error:&error];
 | 
			
		||||
            }];
 | 
			
		||||
            if (!existingSites) {
 | 
			
		||||
                err(@"Lookup of existing sites failed for site: %@, user: %@, error: %@", name, user.userID, error);
 | 
			
		||||
@@ -323,24 +324,24 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    BOOL success = NO;
 | 
			
		||||
    [self.managedObjectContext.undoManager beginUndoGrouping];
 | 
			
		||||
    [self.managedObjectContextIfReady.undoManager beginUndoGrouping];
 | 
			
		||||
    @try {
 | 
			
		||||
 | 
			
		||||
        // Delete existing sites.
 | 
			
		||||
        if (elementsToDelete.count)
 | 
			
		||||
            [self.managedObjectContext performBlockAndWait:^{
 | 
			
		||||
            [self.managedObjectContextIfReady performBlockAndWait:^{
 | 
			
		||||
                [elementsToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
 | 
			
		||||
                    inf(@"Deleting site: %@, it will be replaced by an imported site.", [obj name]);
 | 
			
		||||
                    dbg(@"Deleted Element: %@", [obj debugDescription]);
 | 
			
		||||
                    [self.managedObjectContext deleteObject:obj];
 | 
			
		||||
                    [self.managedObjectContextIfReady deleteObject:obj];
 | 
			
		||||
                }];
 | 
			
		||||
            }];
 | 
			
		||||
 | 
			
		||||
        // Import new sites.
 | 
			
		||||
        if (!user) {
 | 
			
		||||
            [self.managedObjectContext performBlockAndWait:^{
 | 
			
		||||
            [self.managedObjectContextIfReady performBlockAndWait:^{
 | 
			
		||||
                user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class])
 | 
			
		||||
                                                     inManagedObjectContext:self.managedObjectContext];
 | 
			
		||||
                                                     inManagedObjectContext:self.managedObjectContextIfReady];
 | 
			
		||||
                user.name  = userName;
 | 
			
		||||
                user.keyID = [keyIDHex decodeHex];
 | 
			
		||||
            }];
 | 
			
		||||
@@ -355,9 +356,9 @@
 | 
			
		||||
            NSString *exportContent = [siteElements objectAtIndex:5];
 | 
			
		||||
 | 
			
		||||
            // Create new site.
 | 
			
		||||
            [self.managedObjectContext performBlockAndWait:^{
 | 
			
		||||
            [self.managedObjectContextIfReady performBlockAndWait:^{
 | 
			
		||||
                MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:[key.algorithm classNameOfType:type]
 | 
			
		||||
                                                                         inManagedObjectContext:self.managedObjectContext];
 | 
			
		||||
                                                                         inManagedObjectContext:self.managedObjectContextIfReady];
 | 
			
		||||
                element.name     = name;
 | 
			
		||||
                element.user     = user;
 | 
			
		||||
                element.type     = type;
 | 
			
		||||
@@ -385,10 +386,10 @@
 | 
			
		||||
        return MPImportResultSuccess;
 | 
			
		||||
    }
 | 
			
		||||
    @finally {
 | 
			
		||||
        [self.managedObjectContext.undoManager endUndoGrouping];
 | 
			
		||||
        [self.managedObjectContextIfReady.undoManager endUndoGrouping];
 | 
			
		||||
 | 
			
		||||
        if (!success)
 | 
			
		||||
            [self.managedObjectContext.undoManager undoNestedGroup];
 | 
			
		||||
            [self.managedObjectContextIfReady.undoManager undoNestedGroup];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -60,6 +60,7 @@
 | 
			
		||||
- (IBAction)upgradePassword;
 | 
			
		||||
- (IBAction)action:(UIBarButtonItem *)sender;
 | 
			
		||||
- (IBAction)toggleUser;
 | 
			
		||||
- (IBAction)searchOutdatedElements;
 | 
			
		||||
- (IBAction)closeOutdatedAlert;
 | 
			
		||||
- (IBAction)infoOutdatedAlert;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -86,7 +86,7 @@
 | 
			
		||||
                                                                                               action:@selector(editUserName:)]];
 | 
			
		||||
    [self.userNameContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(copyUserName:)]];
 | 
			
		||||
    [self.outdatedAlertBack addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
 | 
			
		||||
                                                                                         action:@selector(searchOutdatedElements:)]];
 | 
			
		||||
                                                                                         action:@selector(infoOutdatedAlert)]];
 | 
			
		||||
 | 
			
		||||
    self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]];
 | 
			
		||||
 | 
			
		||||
@@ -137,12 +137,13 @@
 | 
			
		||||
    [self updateAnimated:animated];
 | 
			
		||||
 | 
			
		||||
    if ([MPAppDelegate get].activeUser)
 | 
			
		||||
        [[MPAppDelegate get].managedObjectContext performBlock:^void() {
 | 
			
		||||
        [[MPAppDelegate get].managedObjectContextIfReady performBlock:^void() {
 | 
			
		||||
            NSError        *error            = nil;
 | 
			
		||||
            NSFetchRequest *migrationRequest = [NSFetchRequest
 | 
			
		||||
             fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
 | 
			
		||||
            migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d", MPAlgorithmDefaultVersion];
 | 
			
		||||
            NSArray *migrationElements = [[MPAppDelegate get].managedObjectContext executeFetchRequest:migrationRequest error:&error];
 | 
			
		||||
            NSArray *migrationElements = [[MPAppDelegate get].managedObjectContextIfReady executeFetchRequest:migrationRequest
 | 
			
		||||
                                                                                                        error:&error];
 | 
			
		||||
            if (!migrationElements) {
 | 
			
		||||
                err(@"While looking for elements to migrate: %@", error);
 | 
			
		||||
                return;
 | 
			
		||||
@@ -652,7 +653,7 @@
 | 
			
		||||
                                }];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (IBAction)searchOutdatedElements:(UITapGestureRecognizer *)sender {
 | 
			
		||||
- (IBAction)searchOutdatedElements {
 | 
			
		||||
 | 
			
		||||
    self.searchDisplayController.searchBar.selectedScopeButtonIndex    = MPSearchScopeOutdated;
 | 
			
		||||
    self.searchDisplayController.searchBar.searchResultsButtonSelected = YES;
 | 
			
		||||
@@ -824,16 +825,16 @@
 | 
			
		||||
                                    // Update password type.
 | 
			
		||||
                                    if ([self.activeElement.algorithm classOfType:type] != self.activeElement.typeClass)
 | 
			
		||||
                                     // Type requires a different class of element.  Recreate the element.
 | 
			
		||||
                                        [[MPAppDelegate managedObjectContext] performBlockAndWait:^{
 | 
			
		||||
                                        [[MPAppDelegate managedObjectContextIfReady] performBlockAndWait:^{
 | 
			
		||||
                                            MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:[self.activeElement.algorithm classNameOfType:type]
 | 
			
		||||
                                                                                                        inManagedObjectContext:[MPAppDelegate managedObjectContext]];
 | 
			
		||||
                                                                                                        inManagedObjectContext:[MPAppDelegate managedObjectContextIfReady]];
 | 
			
		||||
                                            newElement.name     = self.activeElement.name;
 | 
			
		||||
                                            newElement.user     = self.activeElement.user;
 | 
			
		||||
                                            newElement.uses     = self.activeElement.uses;
 | 
			
		||||
                                            newElement.lastUsed = self.activeElement.lastUsed;
 | 
			
		||||
                                            newElement.version  = self.activeElement.version;
 | 
			
		||||
 | 
			
		||||
                                            [[MPAppDelegate managedObjectContext] deleteObject:self.activeElement];
 | 
			
		||||
                                            [[MPAppDelegate managedObjectContextIfReady] deleteObject:self.activeElement];
 | 
			
		||||
                                            self.activeElement = newElement;
 | 
			
		||||
                                        }];
 | 
			
		||||
 | 
			
		||||
@@ -941,7 +942,7 @@
 | 
			
		||||
 | 
			
		||||
    if (navigationType == UIWebViewNavigationTypeLinkClicked) {
 | 
			
		||||
        if ([[[request URL] query] isEqualToString:@"outdated"]) {
 | 
			
		||||
            [self searchOutdatedElements:nil];
 | 
			
		||||
            [self searchOutdatedElements];
 | 
			
		||||
            return NO;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@
 | 
			
		||||
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPElementEntity class])];
 | 
			
		||||
    fetchRequest.sortDescriptors           = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]];
 | 
			
		||||
    self.fetchedResultsController          = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
 | 
			
		||||
                                                                                 managedObjectContext:[MPAppDelegate managedObjectContext]
 | 
			
		||||
                                                                                 managedObjectContext:[MPAppDelegate managedObjectContextIfReady]
 | 
			
		||||
                                                                                   sectionNameKeyPath:nil cacheName:nil];
 | 
			
		||||
    self.fetchedResultsController.delegate = self;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@
 | 
			
		||||
@synthesize wordList = _wordList;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- (void)initAvatarAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user {
 | 
			
		||||
- (void)initializeAvatarAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user {
 | 
			
		||||
 | 
			
		||||
    UIScrollView *alertAvatarScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(12, 30, 260, 150)];
 | 
			
		||||
    alertAvatarScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
 | 
			
		||||
@@ -88,7 +88,7 @@
 | 
			
		||||
    [alertAvatarScrollView setContentOffset:selectedOffset animated:YES];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)initConfirmationAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user {
 | 
			
		||||
- (void)initializeConfirmationAlert:(UIAlertView *)alert forUser:(MPUserEntity *)user {
 | 
			
		||||
 | 
			
		||||
    UIView *container = [[UIView alloc] initWithFrame:CGRectMake(12, 70, 260, 110)];
 | 
			
		||||
    [alert addSubview:container];
 | 
			
		||||
@@ -145,6 +145,15 @@
 | 
			
		||||
        [self initializeWordLabel:wordLabel];
 | 
			
		||||
    }                        recurse:NO];
 | 
			
		||||
 | 
			
		||||
    [[NSNotificationCenter defaultCenter] addObserverForName:PersistentStoreDidChange object:nil queue:nil usingBlock:
 | 
			
		||||
     ^(NSNotification *note) {
 | 
			
		||||
         [self updateUsers];
 | 
			
		||||
     }];
 | 
			
		||||
    [[NSNotificationCenter defaultCenter] addObserverForName:PersistentStoreDidMergeChanges object:nil queue:nil usingBlock:
 | 
			
		||||
     ^(NSNotification *note) {
 | 
			
		||||
         [self updateUsers];
 | 
			
		||||
     }];
 | 
			
		||||
 | 
			
		||||
    [super viewDidLoad];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -195,7 +204,7 @@
 | 
			
		||||
 | 
			
		||||
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([MPUserEntity class])];
 | 
			
		||||
    fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO]];
 | 
			
		||||
    NSArray *users = [[MPAppDelegate managedObjectContext] executeFetchRequest:fetchRequest error:nil];
 | 
			
		||||
    NSArray *users = [[MPAppDelegate managedObjectContextIfReady] executeFetchRequest:fetchRequest error:nil];
 | 
			
		||||
 | 
			
		||||
    // Clean up avatars.
 | 
			
		||||
    for (UIView *subview in [self.avatarsView subviews])
 | 
			
		||||
@@ -276,16 +285,16 @@
 | 
			
		||||
- (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar {
 | 
			
		||||
 | 
			
		||||
    __block MPUserEntity *newUser = nil;
 | 
			
		||||
    [[MPAppDelegate managedObjectContext] performBlockAndWait:^{
 | 
			
		||||
    [[MPAppDelegate managedObjectContextIfReady] performBlockAndWait:^{
 | 
			
		||||
        newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class])
 | 
			
		||||
                                                inManagedObjectContext:[MPAppDelegate managedObjectContext]];
 | 
			
		||||
                                                inManagedObjectContext:[MPAppDelegate managedObjectContextIfReady]];
 | 
			
		||||
    }];
 | 
			
		||||
 | 
			
		||||
    [self showNewUserNameAlertFor:newUser completion:^(BOOL finished) {
 | 
			
		||||
        newUserAvatar.selected = NO;
 | 
			
		||||
        if (!finished)
 | 
			
		||||
            [[MPAppDelegate managedObjectContext] performBlock:^{
 | 
			
		||||
                [[MPAppDelegate managedObjectContext] deleteObject:newUser];
 | 
			
		||||
            [[MPAppDelegate managedObjectContextIfReady] performBlock:^{
 | 
			
		||||
                [[MPAppDelegate managedObjectContextIfReady] deleteObject:newUser];
 | 
			
		||||
            }];
 | 
			
		||||
    }];
 | 
			
		||||
}
 | 
			
		||||
@@ -318,7 +327,7 @@
 | 
			
		||||
    [PearlAlert showAlertWithTitle:@"Choose Your Avatar"
 | 
			
		||||
                           message:@"\n\n\n\n\n\n" viewStyle:UIAlertViewStyleDefault
 | 
			
		||||
                         initAlert:^(UIAlertView *_alert, UITextField *_firstField) {
 | 
			
		||||
                             [self initAvatarAlert:_alert forUser:newUser];
 | 
			
		||||
                             [self initializeAvatarAlert:_alert forUser:newUser];
 | 
			
		||||
                         }
 | 
			
		||||
                 tappedButtonBlock:^(UIAlertView *_alert, NSInteger _buttonIndex) {
 | 
			
		||||
 | 
			
		||||
@@ -335,7 +344,7 @@
 | 
			
		||||
                             @"\n\n\n\n\n\n"
 | 
			
		||||
                         viewStyle:UIAlertViewStyleDefault
 | 
			
		||||
                         initAlert:^void(UIAlertView *__alert, UITextField *__firstField) {
 | 
			
		||||
                             [self initConfirmationAlert:__alert forUser:newUser];
 | 
			
		||||
                             [self initializeConfirmationAlert:__alert forUser:newUser];
 | 
			
		||||
                         }
 | 
			
		||||
                 tappedButtonBlock:^void(UIAlertView *__alert, NSInteger __buttonIndex) {
 | 
			
		||||
                     if (__buttonIndex == [__alert cancelButtonIndex]) {
 | 
			
		||||
@@ -720,8 +729,8 @@
 | 
			
		||||
                         return;
 | 
			
		||||
 | 
			
		||||
                     if (buttonIndex == [sheet destructiveButtonIndex]) {
 | 
			
		||||
                         [[MPAppDelegate get].managedObjectContext performBlockAndWait:^{
 | 
			
		||||
                             [[MPAppDelegate get].managedObjectContext deleteObject:targetedUser];
 | 
			
		||||
                         [[MPAppDelegate get].managedObjectContextIfReady performBlockAndWait:^{
 | 
			
		||||
                             [[MPAppDelegate get].managedObjectContextIfReady deleteObject:targetedUser];
 | 
			
		||||
                         }];
 | 
			
		||||
                         [[MPAppDelegate get] saveContext];
 | 
			
		||||
                         [self updateUsers];
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="2.0" toolsVersion="2549" systemVersion="12A269" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="KZF-fe-y9n">
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <deployment defaultVersion="1296" identifier="iOS"/>
 | 
			
		||||
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="1498"/>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
    <scenes>
 | 
			
		||||
@@ -564,7 +563,7 @@ Your passwords will be AES-encrypted with your master password.</string>
 | 
			
		||||
                                                <rect key="frame" x="20" y="94" width="280" height="31"/>
 | 
			
		||||
                                                <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
 | 
			
		||||
                                                <subviews>
 | 
			
		||||
                                                    <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Tap and hold to save a user name." textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" id="tj6-Ot-Fuh">
 | 
			
		||||
                                                    <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Tap and hold to save a login name." textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" id="tj6-Ot-Fuh">
 | 
			
		||||
                                                        <rect key="frame" x="0.0" y="0.0" width="280" height="31"/>
 | 
			
		||||
                                                        <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
 | 
			
		||||
                                                        <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
 | 
			
		||||
@@ -814,9 +813,9 @@ L4m3P4sSw0rD</string>
 | 
			
		||||
                                        <rect key="frame" x="20" y="60" width="260" height="130"/>
 | 
			
		||||
                                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
 | 
			
		||||
                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
 | 
			
		||||
                                        <string key="text">Some of your sites have outdated passwords.  Tap this alert to see them.
 | 
			
		||||
                                        <string key="text">Some of your sites have outdated passwords.  Tap this alert for more info or       to find them.
 | 
			
		||||
 | 
			
		||||
You should upgrade these sites and update their account's passwords as soon as convenient.</string>
 | 
			
		||||
You should upgrade these sites and update their account's passwords as soon as is convenient.</string>
 | 
			
		||||
                                        <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
 | 
			
		||||
                                        <fontDescription key="fontDescription" type="system" pointSize="12"/>
 | 
			
		||||
                                        <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
 | 
			
		||||
@@ -842,7 +841,7 @@ You should upgrade these sites and update their account's passwords as soon as c
 | 
			
		||||
                                        <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
 | 
			
		||||
                                        <accessibility key="accessibilityConfiguration" hint="" label="Close"/>
 | 
			
		||||
                                        <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
 | 
			
		||||
                                        <state key="normal" image="icon_info.png">
 | 
			
		||||
                                        <state key="normal" image="icon_find.png">
 | 
			
		||||
                                            <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
 | 
			
		||||
                                            <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
 | 
			
		||||
                                        </state>
 | 
			
		||||
@@ -850,7 +849,23 @@ You should upgrade these sites and update their account's passwords as soon as c
 | 
			
		||||
                                            <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
 | 
			
		||||
                                        </state>
 | 
			
		||||
                                        <connections>
 | 
			
		||||
                                            <action selector="infoOutdatedAlert" destination="PQa-Xl-A3x" eventType="touchUpInside" id="U2G-GA-wT2"/>
 | 
			
		||||
                                            <action selector="searchOutdatedElements" destination="PQa-Xl-A3x" eventType="touchUpInside" id="7Wz-49-RQe"/>
 | 
			
		||||
                                        </connections>
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Sfy-hx-kVU">
 | 
			
		||||
                                        <rect key="frame" x="184" y="81" width="22" height="22"/>
 | 
			
		||||
                                        <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
 | 
			
		||||
                                        <accessibility key="accessibilityConfiguration" hint="" label="Close"/>
 | 
			
		||||
                                        <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
 | 
			
		||||
                                        <state key="normal" image="icon_find.png">
 | 
			
		||||
                                            <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
 | 
			
		||||
                                            <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
 | 
			
		||||
                                        </state>
 | 
			
		||||
                                        <state key="highlighted">
 | 
			
		||||
                                            <color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
 | 
			
		||||
                                        </state>
 | 
			
		||||
                                        <connections>
 | 
			
		||||
                                            <action selector="searchOutdatedElements" destination="PQa-Xl-A3x" eventType="touchUpInside" id="4jj-PA-b0I"/>
 | 
			
		||||
                                        </connections>
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                </subviews>
 | 
			
		||||
@@ -1798,7 +1813,7 @@ You could use the word wall for  inspiration in finding a memorable master passw
 | 
			
		||||
        <image name="icon_action.png" width="32" height="32"/>
 | 
			
		||||
        <image name="icon_cancel.png" width="32" height="32"/>
 | 
			
		||||
        <image name="icon_edit.png" width="32" height="32"/>
 | 
			
		||||
        <image name="icon_info.png" width="32" height="32"/>
 | 
			
		||||
        <image name="icon_find.png" width="32" height="32"/>
 | 
			
		||||
        <image name="icon_person.png" width="32" height="32"/>
 | 
			
		||||
        <image name="icon_plus.png" width="32" height="32"/>
 | 
			
		||||
        <image name="icon_up.png" width="32" height="32"/>
 | 
			
		||||
@@ -1833,13 +1848,11 @@ You could use the word wall for  inspiration in finding a memorable master passw
 | 
			
		||||
                <relationship kind="action" name="closeAlert"/>
 | 
			
		||||
                <relationship kind="action" name="closeOutdatedAlert"/>
 | 
			
		||||
                <relationship kind="action" name="copyContent"/>
 | 
			
		||||
                <relationship kind="action" name="copyUserName:" candidateClass="UITapGestureRecognizer"/>
 | 
			
		||||
                <relationship kind="action" name="editPassword"/>
 | 
			
		||||
                <relationship kind="action" name="editUserName:" candidateClass="UILongPressGestureRecognizer"/>
 | 
			
		||||
                <relationship kind="action" name="incrementPasswordCounter"/>
 | 
			
		||||
                <relationship kind="action" name="infoOutdatedAlert"/>
 | 
			
		||||
                <relationship kind="action" name="resetPasswordCounter:" candidateClass="UILongPressGestureRecognizer"/>
 | 
			
		||||
                <relationship kind="action" name="searchOutdatedElements:" candidateClass="UITapGestureRecognizer"/>
 | 
			
		||||
                <relationship kind="action" name="toggleUser"/>
 | 
			
		||||
                <relationship kind="action" name="upgradePassword"/>
 | 
			
		||||
                <relationship kind="outlet" name="actionsTipContainer" candidateClass="UIView"/>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user