2
0

Avoid using object.managedObjectContext - when no strong reference exists to the MOC, it may yield nil.

[FIXED]     Unexpected nil MOCs.
This commit is contained in:
Maarten Billemont 2013-04-30 01:49:53 -04:00
parent 0ee1e176ed
commit 40f34f3d77
15 changed files with 329 additions and 92 deletions

View File

@ -25,7 +25,7 @@
@required
- (NSUInteger)version;
- (BOOL)migrateUser:(MPUserEntity *)user;
- (BOOL)migrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
- (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit;
- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName;

View File

@ -31,12 +31,12 @@
return 0;
}
- (BOOL)migrateUser:(MPUserEntity *)user {
- (BOOL)migrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
NSError *error = nil;
NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
NSArray *migrationElements = [user.managedObjectContext executeFetchRequest:migrationRequest error:&error];
NSArray *migrationElements = [moc executeFetchRequest:migrationRequest error:&error];
if (!migrationElements) {
err(@"While looking for elements to migrate: %@", error);
return NO;

View File

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

View File

@ -77,7 +77,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
[[NSNotificationCenter defaultCenter] postNotificationName:MPSignedOutNotification object:self userInfo:@{ @"animated" : @(animated) }];
}
- (BOOL)signInAsUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password {
- (BOOL)signInAsUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password {
if (password)
NSAssert(![NSThread isMainThread], @"Computing key may not happen from the main thread.");

View File

@ -493,6 +493,8 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
NSString *uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
NSString *type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
NSString *version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
if ([version length])
version = [version substringFromIndex:1]; // Strip the leading colon.
NSString *name = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
NSString *exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
@ -509,6 +511,8 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
[elementsToDelete addObjectsFromArray:existingSites];
[importedSiteElements addObject:@[ lastUsed, uses, type, version, name, exportContent ]];
dbg(@"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, name=%@, exportContent=%@",
lastUsed, uses, type, version, name, exportContent);
}
}

View File

@ -139,12 +139,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
if (sender == self.rememberPasswordItem)
[MPConfig get].rememberLogin = [NSNumber numberWithBool:![[MPConfig get].rememberLogin boolValue]];
if (sender == self.savePasswordItem) {
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserForThread];
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
if ((activeUser.saveKey = !activeUser.saveKey))
[[MPMacAppDelegate get] storeSavedKeyFor:activeUser];
else
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
[activeUser.managedObjectContext saveToStore];
[moc saveToStore];
}
if (sender == self.dialogStyleRegular)
[MPMacConfig get].dialogStyleHUD = @NO;

View File

@ -256,11 +256,12 @@
- (MPElementEntity *)activeElementForThread {
if (!_activeElementOID)
return nil;
return [self activeElementInContext:[MPMacAppDelegate managedObjectContextForThreadIfReady]];
}
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
- (MPElementEntity *)activeElementInContext:(NSManagedObjectContext *)moc {
if (!_activeElementOID)
return nil;
NSError *error;
@ -297,9 +298,10 @@
return;
}
MPElementEntity *activeElement = [self activeElementForThread];
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
MPElementEntity *activeElement = [self activeElementInContext:moc];
[activeElement use];
[activeElement.managedObjectContext saveToStore];
[moc saveToStore];
}
dispatch_async( dispatch_get_main_queue(), ^{

View File

@ -50,6 +50,10 @@
element.version = MPAlgorithmDefaultVersion;
[moc saveToStore];
NSError *error = nil;
if (element.objectID.isTemporaryID && ![moc obtainPermanentIDsForObjects:@[ element ] error:&error])
err(@"Failed to obtain a permanent object ID after creating new element: %@", error);
NSManagedObjectID *elementOID = [element objectID];
dispatch_async( dispatch_get_main_queue(), ^{
MPElementEntity *element_ = (MPElementEntity *)[[MPiOSAppDelegate managedObjectContextForThreadIfReady]
@ -292,12 +296,18 @@
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
MPElementEntity *element = [self elementForTableIndexPath:indexPath];
[element.managedObjectContext performBlockAndWait:^{
NSManagedObjectID *elementOID = [self elementForTableIndexPath:indexPath].objectID;
[MPiOSAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
NSError *error = nil;
MPElementEntity *element = (MPElementEntity *)[context existingObjectWithID:elementOID error:&error];
if (!element) {
err(@"Failed to retrieve element to delete: %@", error);
return;
}
inf(@"Deleting element: %@", element.name);
[element.managedObjectContext deleteObject:element];
[element.managedObjectContext saveToStore];
[context deleteObject:element];
[context saveToStore];
MPCheckpoint( MPCheckpointDeleteElement, @{
@"type" : element.typeName,

View File

@ -71,7 +71,17 @@
- (IBAction)mail:(UIBarButtonItem *)sender {
[[MPiOSAppDelegate get] openFeedbackWithLogs:YES forVC:self];
if ([[MPiOSConfig get].traceMode boolValue]) {
[PearlAlert showAlertWithTitle:@"Hiding Trace Messages" message:
@"Trace-level log messages will not be mailed. "
@"These messages contain sensitive and personal information."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
[[MPiOSAppDelegate get] openFeedbackWithLogs:YES forVC:self];
} cancelTitle:[PearlStrings get].commonButtonOkay otherTitles:nil];
}
else
[[MPiOSAppDelegate get] openFeedbackWithLogs:YES forVC:self];
}
@end

View File

@ -146,15 +146,15 @@
// Needed for when we appear after a modal VC dismisses:
// We can't present until the other modal VC has been fully dismissed and presenting in -viewWillAppear: will fail.
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForThread];
if ([MPAlgorithmDefault migrateUser:activeUser] && !self.suppressOutdatedAlert)
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
if ([MPAlgorithmDefault migrateUser:activeUser inContext:moc] && !self.suppressOutdatedAlert)
[UIView animateWithDuration:0.3f animations:^{
self.outdatedAlertContainer.alpha = 1;
self.suppressOutdatedAlert = YES;
}];
[activeUser.managedObjectContext saveToStore];
} );
[moc saveToStore];
}];
if (![[MPiOSConfig get].actionsTipShown boolValue])
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
@ -506,7 +506,7 @@
@"If you continue, a new password will be generated for this site. "
@"You will then need to update your account's old password to this newly generated password.\n\n"
@"You can reset the counter by holding down on this button."
do:^BOOL(MPElementEntity *activeElement) {
do:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) {
if (![activeElement isKindOfClass:[MPElementGeneratedEntity class]]) {
// Not of a type that supports a password counter.
err(@"Cannot increment password counter: Element is not generated: %@", activeElement.name);
@ -545,7 +545,7 @@
@"You are resetting the site's password counter.\n\n"
@"If you continue, the site's password will change back to its original value. "
@"You will then need to update your account's password back to this original value."
do:^BOOL(MPElementEntity *activeElement_) {
do:^BOOL(MPElementEntity *activeElement_, NSManagedObjectContext *context) {
inf(@"Resetting password counter for: %@", activeElement_.name);
((MPElementGeneratedEntity *)activeElement_).counter = 1;
@ -576,7 +576,8 @@
} );
}
- (void)changeActiveElementWithWarning:(NSString *)warning do:(BOOL (^)(MPElementEntity *activeElement))task; {
- (void)changeActiveElementWithWarning:(NSString *)warning
do:(BOOL (^)(MPElementEntity *activeElement, NSManagedObjectContext *context))task {
[PearlAlert showAlertWithTitle:@"Password Change" message:warning viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
@ -587,7 +588,7 @@
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil];
}
- (void)changeActiveElementWithoutWarningDo:(BOOL (^)(MPElementEntity *activeElement))task; {
- (void)changeActiveElementWithoutWarningDo:(BOOL (^)(MPElementEntity *, NSManagedObjectContext *context))task {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *activeElement = [self activeElementInContext:context];
@ -595,7 +596,7 @@
return;
NSString *oldPassword = [activeElement.content description];
if (!task( activeElement ))
if (!task( activeElement, context ))
return;
NSString *newPassword = [activeElement.content description];
@ -669,7 +670,7 @@
@"This upgrade improves the site's compatibility with the latest version of Master Password.";
[self changeActiveElementWithWarning:warning do:
^BOOL(MPElementEntity *activeElement_) {
^BOOL(MPElementEntity *activeElement_, NSManagedObjectContext *context) {
inf(@"Explicitly migrating element: %@", activeElement_);
[activeElement_ migrateExplicitly:YES];
@ -785,12 +786,12 @@
@"You are about to change the type of this password.\n\n"
@"If you continue, the password for this site will change. "
@"You will need to update your account's old password to the new one."
do:^BOOL(MPElementEntity *activeElement) {
do:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) {
if ([activeElement.algorithm classOfType:type] != activeElement.typeClass) {
// Type requires a different class of element. Recreate the element.
MPElementEntity *newElement
= [NSEntityDescription insertNewObjectForEntityForName:[activeElement.algorithm classNameOfType:type]
inManagedObjectContext:activeElement.managedObjectContext];
inManagedObjectContext:context];
newElement.name = activeElement.name;
newElement.user = activeElement.user;
newElement.uses = activeElement.uses;
@ -798,11 +799,10 @@
newElement.version = activeElement.version;
newElement.loginName = activeElement.loginName;
[activeElement.managedObjectContext deleteObject:activeElement];
[context deleteObject:activeElement];
NSError *error;
if (![newElement.managedObjectContext obtainPermanentIDsForObjects:@[ newElement ]
error:&error])
if (![context obtainPermanentIDsForObjects:@[ newElement ] error:&error])
err(@"Failed to obtain a permanent object ID after changing object type: %@", error);
_activeElementOID = newElement.objectID;
@ -819,15 +819,11 @@
- (void)didSelectElement:(MPElementEntity *)element {
inf(@"Selected: %@", element.name);
NSError *error = nil;
if (element.objectID.isTemporaryID && ![element.managedObjectContext obtainPermanentIDsForObjects:@[ element ] error:&error])
err(@"Failed to obtain a permanent object ID after setting active element: %@", error);
_activeElementOID = element.objectID;
[self closeAlert];
if (element) {
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement) {
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) {
if ([activeElement use] == 1)
[self showAlertWithTitle:@"New Site" message:
PearlString( @"You've just created a password for %@.\n\n"
@ -890,7 +886,7 @@
// Content hasn't changed.
return;
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement_) {
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement_, NSManagedObjectContext *context) {
((MPElementStoredEntity *)activeElement_).content = self.contentField.text;
return YES;
}];
@ -903,7 +899,7 @@
[MPiOSConfig get].loginNameTipShown = @YES;
}
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement) {
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) {
if ([self.loginNameField.text length])
activeElement.loginName = self.loginNameField.text;
else

View File

@ -47,9 +47,13 @@
} options:0];
[avatar onSelect:^(BOOL selected) {
if (selected) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForThread];
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
return;
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
activeUser.avatar = (unsigned)avatar.tag;
[activeUser.managedObjectContext saveToStore];
[moc saveToStore];
}
} options:0];
avatar.selected = (a == [[MPiOSAppDelegate get] activeUserForThread].avatar);
@ -129,8 +133,12 @@
[[MPiOSAppDelegate get] export];
else if (cell == self.changeMPCell) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForThread];
[[MPiOSAppDelegate get] changeMasterPasswordFor:activeUser inContext:activeUser.managedObjectContext didResetBlock:nil];
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
return;
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
[[MPiOSAppDelegate get] changeMasterPasswordFor:activeUser saveInContext:moc didResetBlock:nil];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
@ -140,9 +148,13 @@
- (void)didSelectType:(MPElementType)type {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForThread];
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
return;
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
activeUser.defaultType = type;
[activeUser.managedObjectContext saveToStore];
[moc saveToStore];
self.defaultTypeLabel.text = [[MPiOSAppDelegate get].key.algorithm shortNameOfType:activeUser.defaultType];
}
@ -156,12 +168,16 @@
- (IBAction)didToggleSwitch:(UISwitch *)sender {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForThread];
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
return;
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
if ((activeUser.saveKey = sender.on))
[[MPiOSAppDelegate get] storeSavedKeyFor:activeUser];
else
[[MPiOSAppDelegate get] forgetSavedKeyFor:activeUser];
[activeUser.managedObjectContext saveToStore];
[moc saveToStore];
}
@end

View File

@ -345,20 +345,19 @@
[self.avatarToUserOID setObject:NilToNSNull([user objectID]) forKey:[NSValue valueWithNonretainedObject:avatar]];
if ([_selectedUserOID isEqual:[user objectID]]) {
self.selectedUser = user;
if ([_selectedUserOID isEqual:[user objectID]])
avatar.selected = YES;
}
return avatar;
}
- (void)didToggleUserSelection {
MPUserEntity *selectedUser = [self selectedUserForThread];
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
MPUserEntity *selectedUser = [self selectedUserInContext:moc];
if (!selectedUser)
[self.passwordField resignFirstResponder];
else if ([[MPiOSAppDelegate get] signInAsUser:selectedUser inContext:selectedUser.managedObjectContext usingMasterPassword:nil]) {
else if ([[MPiOSAppDelegate get] signInAsUser:selectedUser saveInContext:moc usingMasterPassword:nil]) {
[self performSegueWithIdentifier:@"MP_Unlock" sender:self];
return;
}
@ -450,7 +449,12 @@
}
// Confirm
[moc saveToStore];
[moc performBlockAndWait:^{
[moc saveToStore];
NSError *error = nil;
if (![moc obtainPermanentIDsForObjects:@[newUser] error:&error])
err(@"Failed to obtain permanent object ID for new user: %@", error);
}];
completion( YES );
[self updateUsers];
@ -535,7 +539,7 @@
UIButton *targetedAvatar = selectedAvatar;
if (!targetedAvatar) {
targetedAvatar = [self findTargetedAvatar];
targetedUser = [self userForAvatar:targetedAvatar];
targetedUser = [self userForAvatar:targetedAvatar inContext:[MPiOSAppDelegate managedObjectContextForThreadIfReady]];
}
[self.avatarsView enumerateSubviews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
@ -619,7 +623,7 @@
[self setSpinnerActive:YES];
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
BOOL unlocked = [[MPiOSAppDelegate get] signInAsUser:[self selectedUserInContext:moc] inContext:moc
BOOL unlocked = [[MPiOSAppDelegate get] signInAsUser:[self selectedUserInContext:moc] saveInContext:moc
usingMasterPassword:self.passwordField.text];
dispatch_async( dispatch_get_main_queue(), ^{
@ -656,11 +660,7 @@
return avatar;
}
- (MPUserEntity *)userForAvatar:(UIButton *)avatar {
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
return nil;
- (MPUserEntity *)userForAvatar:(UIButton *)avatar inContext:(NSManagedObjectContext *)moc {
NSManagedObjectID *userOID = NSNullToNil([self.avatarToUserOID objectForKey:[NSValue valueWithNonretainedObject:avatar]]);
if (!userOID)
@ -984,11 +984,11 @@
if ([self selectedUserForThread])
return;
MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar]];
NSManagedObjectContext *moc = [MPiOSAppDelegate managedObjectContextForThreadIfReady];
MPUserEntity *targetedUser = [self userForAvatar:[self findTargetedAvatar] inContext:moc];
if (!targetedUser)
return;
NSManagedObjectContext *moc = targetedUser.managedObjectContext;
[PearlSheet showSheetWithTitle:targetedUser.name
viewStyle:UIActionSheetStyleBlackTranslucent
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
@ -1008,7 +1008,7 @@
}
if (buttonIndex == [sheet firstOtherButtonIndex])
[[MPiOSAppDelegate get] changeMasterPasswordFor:targetedUser inContext:moc didResetBlock:^{
[[MPiOSAppDelegate get] changeMasterPasswordFor:targetedUser saveInContext:moc didResetBlock:^{
dispatch_async( dispatch_get_main_queue(), ^{
[[self avatarForUser:targetedUser] setSelected:YES];
} );

View File

@ -20,6 +20,6 @@
- (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
- (void)export;
- (void)changeMasterPasswordFor:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc didResetBlock:(void (^)(void))didReset;
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void (^)(void))didReset;
@end

View File

@ -470,9 +470,9 @@
- (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController {
NSString *userName = [[MPiOSAppDelegate get] activeUserForThread].name;
PearlLogLevel logLevel = [[MPiOSConfig get].sendInfo boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
if ([[MPiOSConfig get].traceMode boolValue])
logLevel = PearlLogLevelTrace;
PearlLogLevel logLevel = PearlLogLevelInfo;
if (logs && ([[MPiOSConfig get].sendInfo boolValue] || [[MPiOSConfig get].traceMode boolValue]))
logLevel = PearlLogLevelDebug;
[[[PearlEMail alloc] initForEMailTo:@"Master Password Development <masterpassword@lyndir.com>"
subject:PearlString( @"Feedback for Master Password [%@]",
@ -567,7 +567,7 @@
nil];
}
- (void)changeMasterPasswordFor:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc didResetBlock:(void (^)(void))didReset {
- (void)changeMasterPasswordFor:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc didResetBlock:(void (^)(void))didReset {
[PearlAlert showAlertWithTitle:@"Changing Master Password"
message:

View File

@ -492,28 +492,28 @@ Your passwords will be AES-encrypted with your master password.</string>
<rect key="frame" x="0.0" y="0.0" width="320" height="372"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<mutableString key="text">119-20:51:52 MPiOSAppDelegate.m:36 | INFO : Initializing TestFlight
119-20:51:52 MPiOSAppDelegate.m:70 | INFO : Initializing Google+
119-20:51:52 MPiOSAppDelegate.m:80 | INFO : Initializing Crashlytics
119-20:51:52 MPiOSAppDelegate.m:109 | INFO : Initializing Localytics
119-20:51:53 PearlAppDelegate.m:71 | INFO : Master Password (MasterPassword) 1.4 (1.4.0) (GIT: 1.4-0-g8a4eecd-dirty)
119-20:51:53 MPiOSAppDelegate.m:257 | INFO : Started up with device identifier: A8C51CDA-6F60-4F0C-BFC9-68A08F2F2DD7
119-20:51:59 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] (Re)loading store...
119-20:51:59 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Will load cloud store: 0B3CA2DF-5796-44DF-B5E0-121EC3846464 (definite).
119-20:51:59 PearlConfig.m:193 | INFO : Lock screen will appear
119-20:51:59 MPiOSAppDelegate.m:412 | INFO : Re-activated
119-20:51:59 PearlConfig.m:180 | DEBUG : MPiOSConfig.launchCount = [70 ->] 71
119-20:52:02 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Clearing stores...
119-20:52:03 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Loading store without seeding.
119-20:52:09 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Cloud enabled and successfully loaded cloud store.
119-20:52:09 MPAppDelegate_Store.m:299 | INFO : Using iCloud? 1
119-20:52:12 MPAppDelegate_Key.m:28 | INFO : Found key in keychain for: b55911588b178466be1d6392597e899b8de46f9a
119-20:52:12 MPAppDelegate_Key.m:132 | INFO : Logged in: b55911588b178466be1d6392597e899b8de46f9a
119-20:52:13 MPUnlockViewController.m:229 | INFO : Lock screen will disappear
119-20:52:13 MPMainViewController.m:142 | INFO : Main will appear
119-20:52:16 MPMainViewController.m:734 | INFO : Action: Preferences
119-20:52:17 MPMainViewController.m:187 | INFO : Main will disappear.
</mutableString>
<string key="text">119-20:51:52 MPiOSAppDelegate.m:36 | INFO : Initializing TestFlight
119-20:51:52 MPiOSAppDelegate.m:70 | INFO : Initializing Google+
119-20:51:52 MPiOSAppDelegate.m:80 | INFO : Initializing Crashlytics
119-20:51:52 MPiOSAppDelegate.m:109 | INFO : Initializing Localytics
119-20:51:53 PearlAppDelegate.m:71 | INFO : Master Password (MasterPassword) 1.4 (1.4.0) (GIT: 1.4-0-g8a4eecd-dirty)
119-20:51:53 MPiOSAppDelegate.m:257 | INFO : Started up with device identifier: A8C51CDA-6F60-4F0C-BFC9-68A08F2F2DD7
119-20:51:59 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] (Re)loading store...
119-20:51:59 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Will load cloud store: 0B3CA2DF-5796-44DF-B5E0-121EC3846464 (definite).
119-20:51:59 PearlConfig.m:193 | INFO : Lock screen will appear
119-20:51:59 MPiOSAppDelegate.m:412 | INFO : Re-activated
119-20:51:59 PearlConfig.m:180 | DEBUG : MPiOSConfig.launchCount = [70 ->] 71
119-20:52:02 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Clearing stores...
119-20:52:03 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Loading store without seeding.
119-20:52:09 MPAppDelegate_Store.m:278 | DEBUG : [StoreManager] Cloud enabled and successfully loaded cloud store.
119-20:52:09 MPAppDelegate_Store.m:299 | INFO : Using iCloud? 1
119-20:52:12 MPAppDelegate_Key.m:28 | INFO : Found key in keychain for: b55911588b178466be1d6392597e899b8de46f9a
119-20:52:12 MPAppDelegate_Key.m:132 | INFO : Logged in: b55911588b178466be1d6392597e899b8de46f9a
119-20:52:13 MPUnlockViewController.m:229 | INFO : Lock screen will disappear
119-20:52:13 MPMainViewController.m:142 | INFO : Main will appear
119-20:52:16 MPMainViewController.m:734 | INFO : Action: Preferences
119-20:52:17 MPMainViewController.m:187 | INFO : Main will disappear.
</string>
<color key="textColor" cocoaTouchSystemColor="lightTextColor"/>
<fontDescription key="fontDescription" name="AmericanTypewriter" family="American Typewriter" pointSize="9"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -2916,6 +2916,204 @@ However, it means that anyone who finds your device unlocked can do the same.</s
<image name="ui_textfield.png" width="158" height="34"/>
<image name="unlocked.png" width="84" height="80"/>
</resources>
<classes>
<class className="IASKAppSettingsViewController" superclassName="UITableViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/IASKAppSettingsViewController.h"/>
<relationships>
<relationship kind="outlet" name="delegate"/>
</relationships>
</class>
<class className="MPAppViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPAppViewController.h"/>
<relationships>
<relationship kind="action" name="deblock:" candidateClass="UIButton"/>
<relationship kind="action" name="gorillas:" candidateClass="UIButton"/>
</relationships>
</class>
<class className="MPAppsViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPAppsViewController.h"/>
<relationships>
<relationship kind="action" name="exit"/>
<relationship kind="outlet" name="pagePositionView" candidateClass="UIImageView"/>
</relationships>
</class>
<class className="MPElementListAllViewController" superclassName="MPElementListController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPElementListAllViewController.h"/>
<relationships>
<relationship kind="action" name="add:"/>
<relationship kind="action" name="close:"/>
<relationship kind="outlet" name="navigationBar" candidateClass="UINavigationBar"/>
</relationships>
</class>
<class className="MPElementListController" superclassName="UITableViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPElementListController.h"/>
<relationships>
<relationship kind="outlet" name="delegate"/>
</relationships>
</class>
<class className="MPElementListSearchController" superclassName="MPElementListController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPElementListSearchController.h"/>
<relationships>
<relationship kind="outlet" name="searchDisplayController" candidateClass="UISearchDisplayController"/>
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
</relationships>
</class>
<class className="MPGuideViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPGuideViewController.h"/>
<relationships>
<relationship kind="action" name="close"/>
<relationship kind="action" name="play"/>
<relationship kind="outlet" name="content" candidateClass="UIView"/>
<relationship kind="outlet" name="contentButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="contentText" candidateClass="UITextField"/>
<relationship kind="outlet" name="contentTip" candidateClass="UIView"/>
<relationship kind="outlet" name="contentTipText" candidateClass="UILabel"/>
<relationship kind="outlet" name="largePlayButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="progress" candidateClass="UIProgressView"/>
<relationship kind="outlet" name="siteNameTip" candidateClass="UIView"/>
<relationship kind="outlet" name="smallPlayButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="toolButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="toolTip" candidateClass="UIView"/>
<relationship kind="outlet" name="typeButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="typeTip" candidateClass="UIView"/>
<relationship kind="outlet" name="usernameButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="usernameTip" candidateClass="UIView"/>
</relationships>
</class>
<class className="MPLogsViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPLogsViewController.h"/>
<relationships>
<relationship kind="action" name="close:" candidateClass="UIBarButtonItem"/>
<relationship kind="action" name="mail:" candidateClass="UIBarButtonItem"/>
<relationship kind="action" name="refresh:" candidateClass="UIBarButtonItem"/>
<relationship kind="action" name="toggleLevelControl:" candidateClass="UISegmentedControl"/>
<relationship kind="outlet" name="levelControl" candidateClass="UISegmentedControl"/>
<relationship kind="outlet" name="logView" candidateClass="UITextView"/>
</relationships>
</class>
<class className="MPMainViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPMainViewController.h"/>
<relationships>
<relationship kind="action" name="action:" candidateClass="UIBarButtonItem"/>
<relationship kind="action" name="closeAlert"/>
<relationship kind="action" name="closeOutdatedAlert"/>
<relationship kind="action" name="copyContent"/>
<relationship kind="action" name="editLoginName:" candidateClass="UILongPressGestureRecognizer"/>
<relationship kind="action" name="editPassword"/>
<relationship kind="action" name="incrementPasswordCounter"/>
<relationship kind="action" name="infoOutdatedAlert"/>
<relationship kind="action" name="panHelpDown:" candidateClass="UIPanGestureRecognizer"/>
<relationship kind="action" name="panHelpUp:" candidateClass="UIPanGestureRecognizer"/>
<relationship kind="action" name="resetPasswordCounter:" candidateClass="UILongPressGestureRecognizer"/>
<relationship kind="action" name="searchOutdatedElements"/>
<relationship kind="action" name="toggleUser"/>
<relationship kind="action" name="upgradePassword"/>
<relationship kind="outlet" name="actionsTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="alertBody" candidateClass="UITextView"/>
<relationship kind="outlet" name="alertContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="alertTitle" candidateClass="UILabel"/>
<relationship kind="outlet" name="contentContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="contentField" candidateClass="UITextField"/>
<relationship kind="outlet" name="contentTipBody" candidateClass="UILabel"/>
<relationship kind="outlet" name="contentTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="displayContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="helpContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="helpView" candidateClass="UIWebView"/>
<relationship kind="outlet" name="loginNameContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="loginNameField" candidateClass="UITextField"/>
<relationship kind="outlet" name="loginNameTipBody" candidateClass="UILabel"/>
<relationship kind="outlet" name="loginNameTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="outdatedAlertBack" candidateClass="UIImageView"/>
<relationship kind="outlet" name="outdatedAlertCloseButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="outdatedAlertContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="passwordCounter" candidateClass="UILabel"/>
<relationship kind="outlet" name="passwordEdit" candidateClass="UIButton"/>
<relationship kind="outlet" name="passwordIncrementer" candidateClass="UIButton"/>
<relationship kind="outlet" name="passwordUpgrade" candidateClass="UIButton"/>
<relationship kind="outlet" name="passwordUser" candidateClass="UIButton"/>
<relationship kind="outlet" name="pullDownGesture" candidateClass="UIPanGestureRecognizer"/>
<relationship kind="outlet" name="pullDownView" candidateClass="UIImageView"/>
<relationship kind="outlet" name="pullUpGesture" candidateClass="UIPanGestureRecognizer"/>
<relationship kind="outlet" name="pullUpView" candidateClass="UIImageView"/>
<relationship kind="outlet" name="searchDelegate" candidateClass="MPElementListSearchController"/>
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="siteName" candidateClass="UILabel"/>
<relationship kind="outlet" name="toolTipBody" candidateClass="UILabel"/>
<relationship kind="outlet" name="toolTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="toolTipEditIcon" candidateClass="UIImageView"/>
<relationship kind="outlet" name="typeButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="typeTipContainer" candidateClass="UIView"/>
</relationships>
</class>
<class className="MPPreferencesViewController" superclassName="UITableViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPPreferencesViewController.h"/>
<relationships>
<relationship kind="action" name="didToggleSwitch:" candidateClass="UISwitch"/>
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
<relationship kind="outlet" name="changeMPCell" candidateClass="UITableViewCell"/>
<relationship kind="outlet" name="defaultTypeLabel" candidateClass="UILabel"/>
<relationship kind="outlet" name="exportCell" candidateClass="UITableViewCell"/>
<relationship kind="outlet" name="savePasswordSwitch" candidateClass="UISwitch"/>
</relationships>
</class>
<class className="MPSetupViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPSetupViewController.h"/>
<relationships>
<relationship kind="action" name="close:" candidateClass="UIBarButtonItem"/>
<relationship kind="outlet" name="cloudSwitch" candidateClass="UISwitch"/>
<relationship kind="outlet" name="rememberLoginSwitch" candidateClass="UISwitch"/>
</relationships>
</class>
<class className="MPTypeViewController" superclassName="UITableViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPTypeViewController.h"/>
<relationships>
<relationship kind="outlet" name="recommendedTipContainer" candidateClass="UIView"/>
</relationships>
</class>
<class className="MPUnlockViewController" superclassName="UIViewController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPUnlockViewController.h"/>
<relationships>
<relationship kind="action" name="add:" candidateClass="UIButton"/>
<relationship kind="action" name="emergencyClose:" candidateClass="UIButton"/>
<relationship kind="action" name="emergencyCopy:" candidateClass="UIButton"/>
<relationship kind="action" name="facebook:" candidateClass="UIButton"/>
<relationship kind="action" name="google:" candidateClass="UIButton"/>
<relationship kind="action" name="mail:" candidateClass="UIButton"/>
<relationship kind="action" name="targetedUserAction:" candidateClass="UILongPressGestureRecognizer"/>
<relationship kind="action" name="twitter:" candidateClass="UIButton"/>
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
<relationship kind="outlet" name="createPasswordTipView" candidateClass="UIView"/>
<relationship kind="outlet" name="emergencyActivity" candidateClass="UIActivityIndicatorView"/>
<relationship kind="outlet" name="emergencyContentTipContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="emergencyCounter" candidateClass="UILabel"/>
<relationship kind="outlet" name="emergencyCounterStepper" candidateClass="UIStepper"/>
<relationship kind="outlet" name="emergencyGeneratorContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="emergencyMasterPassword" candidateClass="UITextField"/>
<relationship kind="outlet" name="emergencyName" candidateClass="UITextField"/>
<relationship kind="outlet" name="emergencyPassword" candidateClass="UIButton"/>
<relationship kind="outlet" name="emergencySite" candidateClass="UITextField"/>
<relationship kind="outlet" name="emergencyTypeControl" candidateClass="UISegmentedControl"/>
<relationship kind="outlet" name="nameLabel" candidateClass="UILabel"/>
<relationship kind="outlet" name="newsView" candidateClass="UIWebView"/>
<relationship kind="outlet" name="oldNameLabel" candidateClass="UILabel"/>
<relationship kind="outlet" name="passwordField" candidateClass="UITextField"/>
<relationship kind="outlet" name="passwordFieldLabel" candidateClass="UILabel"/>
<relationship kind="outlet" name="passwordTipLabel" candidateClass="UILabel"/>
<relationship kind="outlet" name="passwordTipView" candidateClass="UIView"/>
<relationship kind="outlet" name="passwordView" candidateClass="UIView"/>
<relationship kind="outlet" name="spinner" candidateClass="UIImageView"/>
<relationship kind="outlet" name="targetedUserActionGesture" candidateClass="UILongPressGestureRecognizer"/>
<relationship kind="outlet" name="tip" candidateClass="UILabel"/>
<relationship kind="outlet" name="uiContainer" candidateClass="UIView"/>
<relationship kind="outlet" name="wordWall" candidateClass="UIView"/>
</relationships>
</class>
<class className="PearlNavigationController" superclassName="UINavigationController">
<source key="sourceIdentifier" type="project" relativePath="./Classes/PearlNavigationController.h"/>
</class>
</classes>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<nil key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>