2
0

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:
Maarten Billemont 2012-08-04 10:16:58 +02:00
parent bc3aa3255e
commit 4cdeab4256
9 changed files with 100 additions and 72 deletions

3
.gitignore vendored
View File

@ -28,3 +28,6 @@ Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
# IPA
/sendipa/*
!/sendipa/sendipa.conf
# Java
MasterPassword/Java/**/target

@ -1 +1 @@
Subproject commit 1614e65222a543a75cdf4e5f757f06e39b5979aa
Subproject commit a7b028ff80cd4768be686a9ef2067dfd31989ce0

View File

@ -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;

View File

@ -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];
}
}

View File

@ -60,6 +60,7 @@
- (IBAction)upgradePassword;
- (IBAction)action:(UIBarButtonItem *)sender;
- (IBAction)toggleUser;
- (IBAction)searchOutdatedElements;
- (IBAction)closeOutdatedAlert;
- (IBAction)infoOutdatedAlert;

View File

@ -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;
}

View File

@ -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;

View File

@ -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];

View File

@ -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"/>