2
0

Compare commits

..

26 Commits

Author SHA1 Message Date
Maarten Billemont
e3963a72ad Press bump. 2013-05-11 20:05:07 -04:00
Maarten Billemont
cefe8d144d Import fix, password type change fix, fancier generator appearance.
[FIXED]     Import of ubiquity changes was using wrong delegate method and MOC.
[FIXED]     Password type change broken when password type causes a class change.
[IMPROVED]  Fancier animation of emergency generator appearance.
2013-05-11 19:43:41 -04:00
Maarten Billemont
076cfb1257 MOC saving improvements, Mac app activation, loading overlay, USM update.
[MOVED]     iOS code from MPAppDelegate_Shared to MPiOSAppDelegate.
[FIXED]     Perform MOC saving in MOC perform blocks.
[FIXED]     Mac: Activate app when showing password window and not active.
[FIXED]     Hide overlay while corruption dialog is up.
[FIXED]     Hide corruption dialog when reloading store.
2013-05-11 08:55:09 -04:00
Maarten Billemont
71e3f44c8c Improved migration, file-based StoreUUID, Mac fixes.
[ADDED]     Migration to file-based StoreUUID.
[IMPROVED]  Cleaner, more modular, migration code.
[FIXED]     Don't hook activation to show password window: too annoying when focus shifts.
[FIXED]     Don't set the active user when just importing ubiquity changes, it causes a log-out.
[ADDED]     Mac: Ability to rebuild iCloud.
[REMOVED]   Mac: wasRunning logic to avoid password window appearance on first activation.
[FIXED]     Mac: Retrieve siteName from main thread.
2013-05-10 11:13:55 -04:00
Maarten Billemont
c36f34346d Output info level logs when sendInfo is enabled to aid in debugging. 2013-05-07 16:16:33 -04:00
Maarten Billemont
cd6b83ffe8 iPhone 5 alignment issue in unlock VC.
[FIXED]     Deselect new user avatar if MOC is unavailable when tapping it.
[FIXED]     Position of avatars & spinner on iPhone 5.
[IMPROVED]  Issue button in review dialog.
2013-05-07 13:25:05 -04:00
Maarten Billemont
d110fd18c1 Started checkpoint and executable name revert.
[ADDED]     "Started" checkpoint that logs some platform environment properties.
[REVERTED]  Executable/app name back to just MasterPassword.
2013-05-07 11:22:01 -04:00
Maarten Billemont
e45b9985c2 Mac: New sites.
[MOVED]     Creation of new elements moved to shared code.
[FIXED]     When switching user, unset active key.
[FIXED]     Synchronize content calculation to avoid race issues while typing.
[ADDED]     Ability to create new sites.
[FIXED]     Unset active element when hitting backspace or escape.
2013-05-07 00:48:48 -04:00
Maarten Billemont
8f4eb6df84 Merge branch 'master' of github.com:Lyndir/MasterPassword 2013-05-03 23:50:57 -04:00
Maarten Billemont
809bd60169 Info.plist fixes.
[FIXED]     Add extensions to Icon as well to CFBundleIconFiles because Xcode doesn't seem to listen to the Apple docs.
[FIXED]     Removed newsstand metadata: this is not a newsstand app.
2013-05-03 11:29:38 -04:00
Maarten Billemont
802c5949da More compatible way of specifying icon files.
[FIXED]     Add extensions to Icon as well to CFBundleIconFiles because Xcode doesn't seem to listen to the Apple docs.
2013-05-03 10:15:27 -04:00
Maarten Billemont
407c5528ee Fix sign comparison of enum. 2013-05-02 22:03:53 -04:00
Maarten Billemont
fed3a2bc10 Fix sign comparison of enum. 2013-05-02 22:03:53 -04:00
Maarten Billemont
7329353c6c Work-around for a weird Core Data bug. 2013-05-02 22:01:18 -04:00
Maarten Billemont
a7c861d1b0 Work-around for a weird Core Data bug. 2013-05-02 22:01:18 -04:00
Maarten Billemont
9af8ab3360 Merge branch 'master' of github.com:Lyndir/MasterPassword 2013-05-02 20:41:46 -04:00
Maarten Billemont
ddb5328019 Merge branch 'master' of github.com:Lyndir/MasterPassword 2013-05-02 20:41:46 -04:00
Maarten Billemont
96d97d95e1 New user on Mac.
[ADDED]     Mac: Support for creating a new user.
2013-05-02 20:40:12 -04:00
Maarten Billemont
ab15694d9a Safer migration, boolean description fix.
[ADDED]     Safer store migration: don't delete the old store, allowing the client to downgrade.
[ADDED]     Log checkpoints and send to TestFlight too.
[FIXED]     Describe booleans as YES/NO, not 1/0.
2013-05-02 11:19:34 -04:00
Maarten Billemont
81312e3ff4 Safer migration, boolean description fix.
[ADDED]     Safer store migration: don't delete the old store, allowing the client to downgrade.
[ADDED]     Log checkpoints and send to TestFlight too.
[FIXED]     Describe booleans as YES/NO, not 1/0.
2013-05-02 11:19:34 -04:00
Maarten Billemont
5e1e88bdeb Fancier password dialog.
[ADDED]     Mac: Shadows on labels so they're easier to read in regular mode.
[ADDED]     Mac: Some "subtle" bling on the password.
2013-04-30 21:10:52 -04:00
Maarten Billemont
9882bf408c Content generation fix.
[UPDATED]   For safer MOC usage API.
[FIXED]     Actually set the active element so that content is generated.
2013-04-30 20:49:42 -04:00
Maarten Billemont
40f34f3d77 Avoid using object.managedObjectContext - when no strong reference exists to the MOC, it may yield nil.
[FIXED]     Unexpected nil MOCs.
2013-04-30 01:49:53 -04:00
Maarten Billemont
0ee1e176ed Don't use temporary objectIDs for state.
[FIXED]     Make sure we have permanent ObjectIDs before storing them in state.
[FIXED]     MP-14
2013-04-30 00:32:30 -04:00
Maarten Billemont
c73f89e4a1 App versioning update.
[UPDATED]   App versioning: version is now: decimal short commit hash, short version is now tag+commit number.
2013-04-29 21:24:45 -04:00
Maarten Billemont
fe9a1cdbc4 Log Inspector and trace mode.
[ADDED]     Log Inspector, shake on preference screen to open log.
[ADDED]     Trace Mode: Log at trace level.
[FIXED]     Log messages weren't being recorded for inclusion in feedback.
2013-04-29 21:15:14 -04:00
43 changed files with 1367 additions and 574 deletions

View File

@@ -170,6 +170,16 @@ OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
**/
@property (nonatomic, readonly) NSDate *crashedOnDate;
/**
* Returns the os version that the application crashed on.
**/
@property (nonatomic, readonly) NSString *OSVersion;
/**
* Returns the os build version that the application crashed on.
**/
@property (nonatomic, readonly) NSString *OSBuildVersion;
@end
/**

View File

@@ -15,13 +15,13 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.0.8</string>
<string>2.0.9</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>iPhoneOS</string>
</array>
<key>CFBundleVersion</key>
<string>0200.08.00</string>
<string>0200.09.00</string>
<key>DTPlatformName</key>
<string>iphoneos</string>
<key>MinimumOSVersion</key>

2
External/Pearl vendored

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

@@ -44,6 +44,10 @@
- (void)setActiveUser:(MPUserEntity *)activeUser {
NSError *error;
if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error])
err(@"Failed to obtain a permanent object ID after setting active user: %@", error);
_activeUserOID = activeUser.objectID;
}

View File

@@ -25,6 +25,8 @@ typedef enum {
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *))mocBlock;
- (UbiquityStoreManager *)storeManager;
- (void)addElementNamed:(NSString *)siteName completion:(void (^)(MPElementEntity *element))completion;
- (MPImportResult)importSites:(NSString *)importedSitesString
askImportPassword:(NSString *(^)(NSString *userName))importPassword
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;

View File

@@ -15,13 +15,25 @@
#define STORE_OPTIONS
#endif
#define MPCloudContainerIdentifier @"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
#define MPMigrationLevelLocalStoreKey @"MPMigrationLevelLocalStoreKey"
#define MPMigrationLevelCloudStoreKey @"MPMigrationLevelCloudStoreKey"
typedef NS_ENUM(NSInteger, MPMigrationLevelLocalStore) {
MPMigrationLevelLocalStoreV1,
MPMigrationLevelLocalStoreV2,
MPMigrationLevelLocalStoreCurrent = MPMigrationLevelLocalStoreV2,
};
typedef NS_ENUM(NSInteger, MPMigrationLevelCloudStore) {
MPMigrationLevelCloudStoreV1,
MPMigrationLevelCloudStoreV2,
MPMigrationLevelCloudStoreV3,
MPMigrationLevelCloudStoreCurrent = MPMigrationLevelCloudStoreV3,
};
@implementation MPAppDelegate_Shared(Store)
#if TARGET_OS_IPHONE
PearlAssociatedObjectProperty(PearlAlert*, HandleCloudContentAlert, handleCloudContentAlert);
PearlAssociatedObjectProperty(PearlAlert*, FixCloudContentAlert, fixCloudContentAlert);
PearlAssociatedObjectProperty(PearlOverlay*, StoreLoading, storeLoading);
#endif
PearlAssociatedObjectProperty(NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext);
PearlAssociatedObjectProperty(NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext);
PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext);
@@ -91,7 +103,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
return storeManager;
storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil
containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
containerIdentifier:MPCloudContainerIdentifier
additionalStoreOptions:@{ STORE_OPTIONS }
delegate:self];
@@ -99,18 +111,21 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification
object:[UIApplication sharedApplication] queue:nil
usingBlock:^(NSNotification *note) {
[self saveContexts];
[[self mainManagedObjectContext] saveToStore];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification
object:[UIApplication sharedApplication] queue:nil
usingBlock:^(NSNotification *note) {
[self saveContexts];
[[self mainManagedObjectContext] saveToStore];
}];
#else
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillTerminateNotification
object:[NSApplication sharedApplication] queue:nil
usingBlock:^(NSNotification *note) {
[self saveContexts];
NSManagedObjectContext *moc = self.mainManagedObjectContextIfReady;
[moc performBlockAndWait:^{
[moc saveToStore];
}];
}];
#endif
@@ -119,33 +134,113 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
- (void)migrateStoreForManager:(UbiquityStoreManager *)manager isCloud:(BOOL)isCloudStore {
[self migrateLocalStoreForManager:manager];
[self migrateLocalStore];
if (isCloudStore)
[self migrateCloudStoreForManager:manager];
[self migrateCloudStore];
}
- (void)migrateLocalStoreForManager:(UbiquityStoreManager *)manager {
- (void)migrateLocalStore {
MPMigrationLevelLocalStore migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
if (migrationLevel >= MPMigrationLevelLocalStoreCurrent)
// Local store up-to-date.
return;
inf(@"Local store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPMigrationLevelLocalStoreCurrent);
if (migrationLevel <= MPMigrationLevelLocalStoreV1)
[self migrateV1LocalStore];
[[NSUserDefaults standardUserDefaults] setInteger:MPMigrationLevelLocalStoreCurrent forKey:MPMigrationLevelLocalStoreKey];
inf(@"Successfully migrated old to new local store.");
}
- (void)migrateCloudStore {
MPMigrationLevelCloudStore migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelCloudStoreKey];
if (migrationLevel >= MPMigrationLevelCloudStoreCurrent)
// Cloud store up-to-date.
return;
inf(@"Cloud store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPMigrationLevelCloudStoreCurrent);
if (migrationLevel <= MPMigrationLevelCloudStoreV1)
[self migrateV1CloudStore];
else if (migrationLevel <= MPMigrationLevelCloudStoreV2)
[self migrateV2CloudStore];
[[NSUserDefaults standardUserDefaults] setInteger:MPMigrationLevelCloudStoreCurrent forKey:MPMigrationLevelCloudStoreKey];
}
- (void)migrateV1CloudStore {
// Migrate cloud enabled preference.
NSNumber *oldCloudEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"iCloudEnabledKey"];
if ([oldCloudEnabled boolValue])
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:USMCloudEnabledKey];
// Migrate cloud store.
NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"];
if (!uuid) {
inf(@"No V1 cloud store to migrate.");
return;
}
inf(@"Migrating V1 cloud store: %@ -> %@", uuid, [self.storeManager valueForKey:@"storeUUID"]);
NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:MPCloudContainerIdentifier];
NSURL *oldCloudContentURL = [[cloudContainerURL
URLByAppendingPathComponent:@"Data" isDirectory:YES]
URLByAppendingPathComponent:uuid isDirectory:YES];
NSURL *oldCloudStoreURL = [[[cloudContainerURL
URLByAppendingPathComponent:@"Database.nosync" isDirectory:YES]
URLByAppendingPathComponent:uuid isDirectory:NO] URLByAppendingPathExtension:@"sqlite"];
[self migrateFromCloudStore:oldCloudStoreURL cloudContent:oldCloudContentURL contentName:uuid];
}
- (void)migrateV2CloudStore {
// Migrate cloud store.
NSString *uuid = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:@"USMStoreUUIDKey"];
if (!uuid) {
inf(@"No V2 cloud store to migrate.");
return;
}
inf(@"Migrating V2 cloud store: %@ -> %@", uuid, [self.storeManager valueForKey:@"storeUUID"]);
NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:MPCloudContainerIdentifier];
NSURL *oldCloudContentURL = [[cloudContainerURL
URLByAppendingPathComponent:@"CloudLogs" isDirectory:YES]
URLByAppendingPathComponent:uuid isDirectory:YES];
NSURL *oldCloudStoreURL = [[[cloudContainerURL
URLByAppendingPathComponent:@"CloudStore.nosync" isDirectory:YES]
URLByAppendingPathComponent:uuid isDirectory:NO] URLByAppendingPathExtension:@"sqlite"];
[self migrateFromCloudStore:oldCloudStoreURL cloudContent:oldCloudContentURL contentName:uuid];
}
- (void)migrateV1LocalStore {
NSURL *applicationFilesDirectory = [[[NSFileManager defaultManager]
URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *oldLocalStoreURL = [[applicationFilesDirectory
URLByAppendingPathComponent:@"MasterPassword" isDirectory:NO] URLByAppendingPathExtension:@"sqlite"];
NSURL *newLocalStoreURL = [manager URLForLocalStore];
if ([newLocalStoreURL isEqual:oldLocalStoreURL]) {
// Old store migration failed earlier and we set the old URL as the manager's local store.
return;
}
if (![[NSFileManager defaultManager] fileExistsAtPath:oldLocalStoreURL.path isDirectory:NO]) {
// No local store to migrate.
return;
}
if ([[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NO]) {
wrn(@"Can't migrate old local store: A new local store already exists.");
inf(@"No V1 local store to migrate.");
return;
}
inf(@"Migrating V1 local store");
[self migrateFromLocalStore:oldLocalStoreURL];
}
- (void)migrateFromLocalStore:(NSURL *)oldLocalStoreURL {
NSURL *newLocalStoreURL = [self.storeManager URLForLocalStore];
if ([[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NO]) {
wrn(@"Can't migrate local store: A new local store already exists.");
return;
}
inf(@"Migrating local store...");
NSError *error = nil;
NSDictionary *oldLocalStoreOptions = @{
STORE_OPTIONS
@@ -159,69 +254,38 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
};
// Create the directory to hold the new local store.
if (![[NSFileManager defaultManager] createDirectoryAtPath:[manager URLForLocalStoreDirectory].path
withIntermediateDirectories:YES attributes:nil error:&error]) {
err(@"While creating directory for new local store: %@", error);
manager.localStoreURL = oldLocalStoreURL;
return;
}
if (![[NSFileManager defaultManager] createDirectoryAtPath:[self.storeManager URLForLocalStoreDirectory].path
withIntermediateDirectories:YES attributes:nil error:&error])
err(@"While creating directory for new local store: %@", error);
if (![manager copyMigrateStore:oldLocalStoreURL withOptions:oldLocalStoreOptions
toStore:newLocalStoreURL withOptions:newLocalStoreOptions
error:nil cause:nil context:nil]) {
manager.localStoreURL = oldLocalStoreURL;
if (![self.storeManager copyMigrateStore:oldLocalStoreURL withOptions:oldLocalStoreOptions
toStore:newLocalStoreURL withOptions:newLocalStoreOptions
error:nil cause:nil context:nil])
return;
}
inf(@"Successfully migrated old to new local store.");
inf(@"Successfully migrated to new local store.");
}
- (void)migrateCloudStoreForManager:(UbiquityStoreManager *)manager {
- (void)migrateFromCloudStore:(NSURL *)oldCloudStoreURL cloudContent:(NSURL *)oldCloudContentURL contentName:(NSString *)contentName {
// Migrate cloud enabled preference.
NSNumber *oldCloudEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"iCloudEnabledKey"];
if ([oldCloudEnabled boolValue]) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:USMCloudEnabledKey];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"iCloudEnabledKey"];
}
// Migrate cloud store.
NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"];
if (!uuid)
// No old cloud store to migrate.
return;
if (![manager cloudSafeForSeeding]) {
wrn(@"Can't migrate old cloud store: A new cloud store already exists.");
if (![self.storeManager cloudSafeForSeeding]) {
inf(@"Can't migrate cloud store: A new cloud store already exists.");
return;
}
NSURL *cloudContainerURL = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"];
NSURL *newCloudStoreURL = [manager URLForCloudStore];
NSURL *newCloudContentURL = [manager URLForCloudContent];
NSURL *oldCloudContentURL = [[cloudContainerURL URLByAppendingPathComponent:@"Data" isDirectory:YES]
URLByAppendingPathComponent:uuid isDirectory:YES];
NSURL *oldCloudStoreDirectoryURL = [cloudContainerURL URLByAppendingPathComponent:@"Database.nosync" isDirectory:YES];
NSURL *oldCloudStoreURL = [[oldCloudStoreDirectoryURL URLByAppendingPathComponent:uuid isDirectory:NO]
URLByAppendingPathExtension:@"sqlite"];
inf(@"Migrating cloud store: %@ -> %@", uuid, [manager valueForKey:@"storeUUID"]);
NSURL *newCloudStoreURL = [self.storeManager URLForCloudStore];
NSURL *newCloudContentURL = [self.storeManager URLForCloudContent];
NSError *error = nil;
NSDictionary *oldCloudStoreOptions = @{
STORE_OPTIONS
// This is here in an attempt to have iCloud recreate the old store file from
// the baseline and transaction logs from the iCloud account.
// In my tests however only the baseline was used to recreate the store which then ended up being empty.
NSPersistentStoreUbiquitousContentNameKey : uuid,
NSPersistentStoreUbiquitousContentNameKey : contentName,
NSPersistentStoreUbiquitousContentURLKey : oldCloudContentURL,
// So instead, we'll just open up the old store as read-only, if it exists.
NSReadOnlyPersistentStoreOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES
};
NSDictionary *newCloudStoreOptions = @{
STORE_OPTIONS
NSPersistentStoreUbiquitousContentNameKey : [manager valueForKey:@"contentName"],
NSPersistentStoreUbiquitousContentNameKey : [self.storeManager valueForKey:@"contentName"],
NSPersistentStoreUbiquitousContentURLKey : newCloudContentURL,
NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES
@@ -229,48 +293,32 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
// Create the directory to hold the new cloud store.
// This is only necessary if we want to try to rebuild the old store. See comment above about how that failed.
if (![[NSFileManager defaultManager] createDirectoryAtPath:oldCloudStoreDirectoryURL.path
withIntermediateDirectories:YES attributes:nil error:&error]) {
err(@"While creating directory for old cloud store: %@", error);
return;
}
if (![[NSFileManager defaultManager] createDirectoryAtPath:[manager URLForCloudStoreDirectory].path
withIntermediateDirectories:YES attributes:nil error:&error]) {
err(@"While creating directory for new cloud store: %@", error);
return;
}
if (![[NSFileManager defaultManager] createDirectoryAtPath:[oldCloudStoreURL URLByDeletingLastPathComponent].path
withIntermediateDirectories:YES attributes:nil error:&error])
err(@"While creating directory for old cloud store: %@", error);
if (![[NSFileManager defaultManager] createDirectoryAtPath:oldCloudContentURL.path
withIntermediateDirectories:YES attributes:nil error:&error])
err(@"While creating directory for old cloud content: %@", error);
if (![[NSFileManager defaultManager] createDirectoryAtPath:[self.storeManager URLForCloudStoreDirectory].path
withIntermediateDirectories:YES attributes:nil error:&error])
err(@"While creating directory for new cloud store: %@", error);
if (![[NSFileManager defaultManager] createDirectoryAtPath:[self.storeManager URLForCloudContent].path
withIntermediateDirectories:YES attributes:nil error:&error])
err(@"While creating directory for new cloud content: %@", error);
if (![manager copyMigrateStore:oldCloudStoreURL withOptions:oldCloudStoreOptions
toStore:newCloudStoreURL withOptions:newCloudStoreOptions
error:nil cause:nil context:nil])
if (![self.storeManager copyMigrateStore:oldCloudStoreURL withOptions:oldCloudStoreOptions
toStore:newCloudStoreURL withOptions:newCloudStoreOptions
error:nil cause:nil context:nil])
return;
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"LocalUUIDKey"];
inf(@"Successfully migrated old to new cloud store.");
}
- (void)saveContexts {
NSManagedObjectContext *mainManagedObjectContext = self.mainManagedObjectContext;
[mainManagedObjectContext performBlockAndWait:^{
NSError *error = nil;
if (![mainManagedObjectContext save:&error])
err(@"While saving main context: %@", error);
}];
NSManagedObjectContext *privateManagedObjectContext = [self privateManagedObjectContextIfReady];
[privateManagedObjectContext performBlockAndWait:^{
NSError *error = nil;
if (![privateManagedObjectContext save:&error])
err(@"While saving private context: %@", error);
}];
inf(@"Successfully migrated to new cloud store.");
}
#pragma mark - UbiquityStoreManagerDelegate
- (NSManagedObjectContext *)managedObjectContextForUbiquityStoreManager:(UbiquityStoreManager *)usm {
- (NSManagedObjectContext *)managedObjectContextForUbiquityChangesInManager:(UbiquityStoreManager *)manager {
return [self privateManagedObjectContextIfReady];
return [self mainManagedObjectContextIfReady];
}
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager log:(NSString *)message {
@@ -280,15 +328,13 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore {
self.privateManagedObjectContext = nil;
self.mainManagedObjectContext = nil;
NSManagedObjectContext *moc = [self mainManagedObjectContextIfReady];
[moc performBlockAndWait:^{
[moc saveToStore];
#if TARGET_OS_IPHONE
dispatch_async( dispatch_get_main_queue(), ^{
if (![self.storeLoading isVisible])
self.storeLoading = [PearlOverlay showOverlayWithTitle:@"Opening Your Data"];
} );
#endif
self.privateManagedObjectContext = nil;
self.mainManagedObjectContext = nil;
}];
[self migrateStoreForManager:manager isCloud:isCloudStore];
}
@@ -307,6 +353,20 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
[privateManagedObjectContext performBlockAndWait:^{
privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
privateManagedObjectContext.persistentStoreCoordinator = coordinator;
// dbg(@"===");
// NSError *error;
// for (NSEntityDescription *entityDescription in [coordinator.managedObjectModel entities]) {
// dbg(@"Entities: %@", entityDescription.name);
// NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:entityDescription.name];
// NSArray *entities = [privateManagedObjectContext executeFetchRequest:request error:&error];
// if (!entities)
// err(@" - Error: %@", error);
// else
// for (id entity in entities)
// dbg(@" - %@", [entity debugDescription]);
// }
// dbg(@"===");
}];
NSManagedObjectContext *mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
@@ -314,12 +374,6 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
self.privateManagedObjectContext = privateManagedObjectContext;
self.mainManagedObjectContext = mainManagedObjectContext;
#if TARGET_OS_IPHONE
[self.handleCloudContentAlert cancelAlertAnimated:YES];
[self.fixCloudContentAlert cancelAlertAnimated:YES];
[self.storeLoading cancelOverlay];
#endif
}
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didEncounterError:(NSError *)error cause:(UbiquityStoreErrorCause)cause
@@ -333,48 +387,45 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
} );
}
- (BOOL)ubiquityStoreManager:(UbiquityStoreManager *)manager handleCloudContentCorruptionWithHealthyStore:(BOOL)storeHealthy {
#pragma mark - Utilities
#if TARGET_OS_IPHONE
if (manager.cloudEnabled && !storeHealthy && !([self.handleCloudContentAlert.alertView isVisible] || [self.fixCloudContentAlert.alertView isVisible]))
- (void)addElementNamed:(NSString *)siteName completion:(void (^)(MPElementEntity *element))completion {
if (![siteName length]) {
completion( nil );
return;
}
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity *activeUser = [self activeUserInContext:moc];
assert(activeUser);
MPElementType type = activeUser.defaultType;
if (!type)
type = activeUser.defaultType = MPElementTypeGeneratedLong;
NSString *typeEntityClassName = [MPAlgorithmDefault classNameOfType:type];
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityClassName
inManagedObjectContext:moc];
element.name = siteName;
element.user = activeUser;
element.type = type;
element.lastUsed = [NSDate date];
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(), ^{
[self showCloudContentAlert];
completion( (MPElementEntity *)[[MPAppDelegate_Shared managedObjectContextForThreadIfReady] objectRegisteredForID:elementOID] );
} );
#endif
return NO;
}];
}
#if TARGET_OS_IPHONE
- (void)showCloudContentAlert {
__weak MPAppDelegate_Shared *wSelf = self;
[self.handleCloudContentAlert cancelAlertAnimated:NO];
self.handleCloudContentAlert = [PearlAlert showActivityWithTitle:@"iCloud Sync Problem" message:
@"Waiting for your other device to autocorrect the problem..."
initAlert:^(UIAlertView *alert) {
[alert addButtonWithTitle:@"Fix Now"];
}];
self.handleCloudContentAlert.tappedButtonBlock = ^(UIAlertView *alert, NSInteger buttonIndex) {
wSelf.fixCloudContentAlert = [PearlAlert showAlertWithTitle:@"Fix iCloud Now" message:
@"This problem can usually be autocorrected by opening the app on another device where you recently made changes.\n"
@"You can correct the problem from this device anyway, but recent changes made on another device might get lost."
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:
^(UIAlertView *alert_, NSInteger buttonIndex_) {
if (buttonIndex_ == alert_.cancelButtonIndex)
[wSelf showCloudContentAlert];
if (buttonIndex_ == [alert_ firstOtherButtonIndex])
[wSelf.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:YES];
}
cancelTitle:[PearlStrings get].commonButtonBack otherTitles:@"Fix Anyway", nil];
};
}
#endif
#pragma mark - Import / Export
- (MPImportResult)importSites:(NSString *)importedSitesString
askImportPassword:(NSString *(^)(NSString *userName))importPassword
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword {
@@ -493,6 +544,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 +562,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

@@ -13,11 +13,12 @@
- (BOOL)saveToStore {
NSError *error;
if (![self save:&error]) {
err(@"While saving: %@", error);
return NO;
}
__block BOOL success = NO;
[self performBlockAndWait:^{
NSError *error = nil;
if (!(success = [self save:&error]))
err(@"While saving: %@", error);
}];
return !self.parentContext || [self.parentContext saveToStore];
}

View File

@@ -68,6 +68,8 @@ typedef enum {
#define MPCheckpointApps @"MPCheckpointApps"
#define MPCheckpointApp @"MPCheckpointApp"
#define MPCheckpointEmergencyGenerator @"MPCheckpointEmergencyGenerator"
#define MPCheckpointLogs @"MPCheckpointLogs"
#define MPCheckpointStarted @"MPCheckpointStarted"
#define MPSignedInNotification @"MPSignedInNotification"
#define MPSignedOutNotification @"MPSignedOutNotification"
@@ -77,7 +79,11 @@ typedef enum {
static void MPCheckpoint(NSString *checkpoint, NSDictionary *attributes) {
inf(@"%@: %@", checkpoint, attributes);
#ifdef LOCALYTICS
[[LocalyticsSession sharedLocalyticsSession] tagEvent:checkpoint attributes:attributes];
#endif
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight passCheckpoint:checkpoint];
#endif
}

View File

@@ -25,9 +25,10 @@
@property(nonatomic, weak) IBOutlet NSMenuItem *dialogStyleRegular;
@property(nonatomic, weak) IBOutlet NSMenuItem *dialogStyleHUD;
- (IBAction)activate:(id)sender;
- (IBAction)showPasswordWindow;
- (IBAction)togglePreference:(NSMenuItem *)sender;
- (IBAction)newUser:(NSMenuItem *)sender;
- (IBAction)lock:(id)sender;
- (IBAction)rebuildCloud:(id)sender;
@end

View File

@@ -11,12 +11,6 @@
#import "MPAppDelegate_Store.h"
#import <Carbon/Carbon.h>
@interface MPMacAppDelegate()
@property(nonatomic) BOOL wasRunning;
@end
@implementation MPMacAppDelegate
#pragma clang diagnostic push
@@ -46,7 +40,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
// Check which hotkey this was.
if (hotKeyID.signature == MPShowHotKey.signature && hotKeyID.id == MPShowHotKey.id) {
[((__bridge MPMacAppDelegate *)userData) activate:nil];
[((__bridge MPMacAppDelegate *)userData) showPasswordWindow];
return noErr;
}
if (hotKeyID.signature == MPLockHotKey.signature && hotKeyID.id == MPLockHotKey.id) {
@@ -92,19 +86,24 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
@"Then give iCloud some time to sync the new user to your Mac.";
}
MPUserEntity *activeUser = self.activeUserForThread;
for (MPUserEntity *user in users) {
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector(selectUser:) keyEquivalent:@""];
[userItem setTarget:self];
[userItem setRepresentedObject:[user objectID]];
[[self.usersItem submenu] addItem:userItem];
if ([user.name isEqualToString:[MPMacConfig get].usedUserName])
if (!activeUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])
[self selectUser:userItem];
}
[self updateMenuItems];
}
- (void)selectUser:(NSMenuItem *)item {
[self signOutAnimated:NO];
NSError *error = nil;
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
self.activeUser = (MPUserEntity *)[moc existingObjectWithID:[item representedObject] error:&error];
@@ -120,18 +119,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[self.statusItem popUpStatusItemMenu:self.statusMenu];
}
- (IBAction)activate:(id)sender {
if (![self activeUserForThread])
// No user, can't activate.
return;
if ([[NSApplication sharedApplication] isActive])
[self applicationDidBecomeActive:nil];
else
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}
- (IBAction)togglePreference:(NSMenuItem *)sender {
if (sender == self.useICloudItem)
@@ -139,12 +126,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;
@@ -153,6 +141,35 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
}
- (IBAction)newUser:(NSMenuItem *)sender {
NSAlert *alert = [NSAlert alertWithMessageText:@"New User"
defaultButton:@"Create User" alternateButton:nil otherButton:@"Cancel"
informativeTextWithFormat:@"To begin, enter your full name.\n\n"
@"IMPORTANT: Enter your name correctly, including the right capitalization, "
@"as you would on an official document."];
NSTextField *nameField = [[NSTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
[alert setAccessoryView:nameField];
[alert layout];
[nameField becomeFirstResponder];
if ([alert runModal] != NSAlertDefaultReturn)
return;
NSString *name = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
inManagedObjectContext:moc];
newUser.name = name;
[moc saveToStore];
NSError *error = nil;
if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
err(@"Failed to obtain permanent object ID for new user: %@", error);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateUsers];
[self setActiveUser:newUser];
[self showPasswordWindow];
}];
}];
}
- (IBAction)lock:(id)sender {
@@ -160,6 +177,16 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
self.key = nil;
}
- (IBAction)rebuildCloud:(id)sender {
if ([[NSAlert alertWithMessageText:@"iCloud Truth Sync" defaultButton:@"Continue"
alternateButton:nil otherButton:@"Cancel"
informativeTextWithFormat:@"This action will force all your iCloud enabled devices to revert to this device's version of the truth."
@"\n\nThis is only necessary if you notice that your devices aren't syncing properly anymore. "
"Any data on other devices not available from here will be lost."] runModal] == NSAlertDefaultReturn)
[self.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:NO];
}
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
[[NSNotificationCenter defaultCenter]
@@ -188,12 +215,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
self.statusItem.target = self;
self.statusItem.action = @selector(showMenu);
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
[self updateUsers];
}];
[[NSNotificationCenter defaultCenter]
addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil usingBlock:
addObserverForName:USMStoreDidImportChangesNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
[self updateUsers];
}];
@@ -315,20 +342,24 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)applicationDidBecomeActive:(NSNotification *)notification {
[self showPasswordWindow];
// [self showPasswordWindow];
}
- (void)showPasswordWindow {
- (IBAction)showPasswordWindow {
// If no user, can't activate.
if (![self activeUserForThread])
return;
// Activate the app if not active.
if (![[NSApplication sharedApplication] isActive])
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
// Don't show window if we weren't already running (ie. if we haven't been activated before).
if (!self.wasRunning)
self.wasRunning = YES;
else {
if (!self.passwordWindow)
self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
[self.passwordWindow showWindow:self];
}
if (!self.passwordWindow)
self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
[self.passwordWindow showWindow:self];
}
- (void)applicationWillResignActive:(NSNotification *)notification {

View File

@@ -13,12 +13,14 @@
#define MPAlertUnlockMP @"MPAlertUnlockMP"
#define MPAlertIncorrectMP @"MPAlertIncorrectMP"
#define MPAlertCreateSite @"MPAlertCreateSite"
@interface MPPasswordWindowController()
@property(nonatomic) BOOL inProgress;
@property(nonatomic) BOOL siteFieldPreventCompletion;
@property(nonatomic, strong) NSOperationQueue *backgroundQueue;
@end
@implementation MPPasswordWindowController {
@@ -32,6 +34,9 @@
else
self.window.styleMask = NSTexturedBackgroundWindowMask | NSResizableWindowMask | NSTitledWindowMask | NSClosableWindowMask;
self.backgroundQueue = [NSOperationQueue new];
self.backgroundQueue.maxConcurrentOperationCount = 1;
[self setContent:@""];
[self.tipField setStringValue:@""];
@@ -48,6 +53,8 @@
// } forKeyPath:@"key" options:NSKeyValueObservingOptionInitial context:nil];
[[NSNotificationCenter defaultCenter]
addObserverForName:NSWindowDidBecomeKeyNotification object:self.window queue:nil usingBlock:^(NSNotification *note) {
if (![MPMacAppDelegate managedObjectContextForThreadIfReady])
[self waitUntilStoreLoaded];
if (!self.inProgress)
[self unlock];
[self.siteField selectText:self];
@@ -58,28 +65,37 @@
}];
[[NSNotificationCenter defaultCenter]
addObserverForName:MPSignedOutNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
_activeElementOID = nil;
[self.window close];
}];
[[NSNotificationCenter defaultCenter]
addObserverForName:USMStoreDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
[self waitUntilStoreLoaded];
}];
[super windowDidLoad];
}
- (void)waitUntilStoreLoaded {
}
- (void)unlock {
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserForThread];
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
if (!activeUser)
// No user to sign in with.
return;
if ([MPMacAppDelegate get].key)
// Already logged in.
return;
if ([[MPMacAppDelegate get] signInAsUser:activeUser usingMasterPassword:nil])
if ([[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc usingMasterPassword:nil])
// Load the key from the keychain.
return;
if (![MPMacAppDelegate get].key)
// Ask the user to set the key through his master password.
dispatch_async( dispatch_get_main_queue(), ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if ([MPMacAppDelegate get].key)
return;
@@ -97,7 +113,7 @@
[passwordField becomeFirstResponder];
[alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertUnlockMP];
} );
}];
}
- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
@@ -107,11 +123,11 @@
return;
}
if (contextInfo == MPAlertUnlockMP) {
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserForThread];
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
switch (returnCode) {
case NSAlertAlternateReturn:
case NSAlertAlternateReturn: {
// "Change" button.
{
NSInteger returnCode_ = [[NSAlert
alertWithMessageText:@"Changing Master Password" defaultButton:nil
alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil informativeTextWithFormat:
@@ -126,39 +142,48 @@
activeUser.keyID = nil;
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
[[MPMacAppDelegate get] signOutAnimated:YES];
[moc saveToStore];
}
}
break;
}
case NSAlertOtherReturn:
case NSAlertOtherReturn: {
// "Cancel" button.
[self.window close];
return;
}
case NSAlertDefaultReturn: {
// "Unlock" button.
self.contentContainer.alphaValue = 0;
[self.progressView startAnimation:nil];
self.inProgress = YES;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0 ), ^{
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser
usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
NSString *password = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc_) {
NSError *error = nil;
MPUserEntity *activeUser_ = (MPUserEntity *)[moc_ existingObjectWithID:activeUser.objectID error:&error];
if (!activeUser_)
err(@"Failed to retrieve active use while logging in: %@", error);
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc_
usingMasterPassword:password];
self.inProgress = NO;
dispatch_async( dispatch_get_main_queue(), ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.progressView stopAnimation:nil];
if (success)
self.contentContainer.alphaValue = 1;
else {
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
NSLocalizedDescriptionKey : PearlString( @"Incorrect master password for user %@",
activeUser.name )
NSLocalizedDescriptionKey : PearlString( @"Incorrect master password for user %@", activeUser.name )
}]] beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertIncorrectMP];
}
} );
} );
}];
}];
break;
}
default:
@@ -167,12 +192,27 @@
return;
}
if (contextInfo == MPAlertCreateSite) {
switch (returnCode) {
case NSAlertDefaultReturn: {
[[MPMacAppDelegate get] addElementNamed:[self.siteField stringValue] completion:^(MPElementEntity *element) {
if (element) {
_activeElementOID = element.objectID;
[self trySiteWithAction:NO];
}
}];
break;
}
default:
break;
}
}
}
- (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words
forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index {
NSString *query = [[control stringValue] substringWithRange:charRange];
NSString *query = [[textView string] substringWithRange:charRange];
if (![query length] || ![MPMacAppDelegate get].key)
return nil;
@@ -185,20 +225,22 @@
NSError *error = nil;
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
if (error)
if (!siteResults)
err(@"While fetching elements for completion: %@", error);
if (siteResults) {
else if ([siteResults count]) {
_activeElementOID = ((NSManagedObject *)[siteResults objectAtIndex:0]).objectID;
for (MPElementEntity *element in siteResults)
[mutableResults addObject:element.name];
//[mutableResults addObject:query]; // For when the app should be able to create new sites.
}
else
_activeElementOID = nil;
}];
if ([mutableResults count] == 1) {
if ([mutableResults count] < 2) {
//[textView setString:[(MPElementEntity *)[siteResults objectAtIndex:0] name]];
//[textView setSelectedRange:NSMakeRange( [query length], [[textView string] length] - [query length] )];
[self trySiteAndCopyContent:NO];
[self trySiteWithAction:NO];
}
return mutableResults;
@@ -206,29 +248,29 @@
- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector {
if (commandSelector == @selector(cancel:)) {
if (commandSelector == @selector(cancel:)) { // Escape without completion.
[self.window close];
return YES;
}
if ((self.siteFieldPreventCompletion = [NSStringFromSelector( commandSelector ) hasPrefix:@"delete"]))
if ((self.siteFieldPreventCompletion = [NSStringFromSelector( commandSelector ) hasPrefix:@"delete"])) { // Backspace any time.
_activeElementOID = nil;
[self trySiteWithAction:NO];
return NO;
if (commandSelector == @selector(insertNewline:)) {
[self trySiteAndCopyContent:YES];
}
if (commandSelector == @selector(insertNewline:)) { // Return without completion.
[self trySiteWithAction:YES];
return YES;
}
return NO;
}
- (void)copyContents {
}
- (void)controlTextDidEndEditing:(NSNotification *)note {
if (note.object != self.siteField)
return;
[self trySiteAndCopyContent:NO];
[self trySiteWithAction:NO];
}
- (void)controlTextDidChange:(NSNotification *)note {
@@ -237,12 +279,18 @@
return;
// Update the site content as the site name changes.
BOOL enterPressed = [[NSApp currentEvent] type] == NSKeyDown &&
[[[NSApp currentEvent] charactersIgnoringModifiers] isEqualToString:@"\r"];
[self trySiteAndCopyContent:enterPressed];
if (enterPressed)
if ([[NSApp currentEvent] type] == NSKeyDown &&
[[[NSApp currentEvent] charactersIgnoringModifiers] isEqualToString:@"\r"]) { // Return while completing.
[self trySiteWithAction:YES];
return;
}
if ([[NSApp currentEvent] type] == NSKeyDown &&
[[[NSApp currentEvent] charactersIgnoringModifiers] characterAtIndex:0] == 0x1b) { // Escape while completing.
_activeElementOID = nil;
[self trySiteWithAction:NO];
return;
}
if (self.siteFieldPreventCompletion) {
self.siteFieldPreventCompletion = NO;
@@ -256,11 +304,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;
@@ -281,66 +330,75 @@
}]];
}
- (void)trySiteAndCopyContent:(BOOL)copyContent {
- (void)trySiteWithAction:(BOOL)doAction {
[self setContent:@""];
[self.tipField setStringValue:@"Generating..."];
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
NSString *siteName = [self.siteField stringValue];
[self.backgroundQueue addOperationWithBlock:^{
NSString *content = [[self activeElementForThread].content description];
if (!content)
content = @"";
if (copyContent) {
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
if (![[NSPasteboard generalPasteboard] setString:content forType:NSPasteboardTypeString]) {
wrn(@"Couldn't copy password to pasteboard.");
dbg(@"name: %@, action: %d", siteName, doAction);
if (doAction) {
if ([content length]) {
// Performing action while content is available. Copy it.
[self copyContent:content];
}
else if ([siteName length]) {
// Performing action without content but a site name is written.
[self createNewSite:siteName];
return;
}
MPElementEntity *activeElement = [self activeElementForThread];
[activeElement use];
[activeElement.managedObjectContext saveToStore];
}
dispatch_async( dispatch_get_main_queue(), ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self setContent:content];
self.tipField.alphaValue = 1;
if (!copyContent)
if ([content length] == 0) {
if ([siteName length])
[self.tipField setStringValue:@"Hit ⌤ (ENTER) to create a new site."];
else
[self.tipField setStringValue:@""];
}
else if (!doAction)
[self.tipField setStringValue:@"Hit ⌤ (ENTER) to copy the password."];
else {
[self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."];
dispatch_time_t popTime = dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) );
dispatch_after( popTime, dispatch_get_main_queue(), ^{
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.2f];
[self.tipField.animator setAlphaValue:0];
[NSAnimationContext endGrouping];
} );
}
} );
} );
}];
}];
}
// For when the app should be able to create new sites.
/*
else
[[MPMacAppDelegate get].managedObjectContext performBlock:^{
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPElementGeneratedEntity class])
inManagedObjectContext:[MPMacAppDelegate get].managedObjectContext];
assert([element isKindOfClass:ClassFromMPElementType(element.type)]);
assert([MPMacAppDelegate get].keyID);
element.name = siteName;
element.keyID = [MPMacAppDelegate get].keyID;
NSString *description = [element.content description];
[element use];
dispatch_async(dispatch_get_main_queue(), ^{
[self setContent:description];
});
}];
*/
- (void)copyContent:(NSString *)content {
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
if (![[NSPasteboard generalPasteboard] setString:content forType:NSPasteboardTypeString]) {
wrn(@"Couldn't copy password to pasteboard.");
return;
}
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
MPElementEntity *activeElement = [self activeElementInContext:moc];
[activeElement use];
[moc saveToStore];
}
- (void)createNewSite:(NSString *)siteName {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSAlert *alert = [NSAlert alertWithMessageText:@"Create site?"
defaultButton:@"Create" alternateButton:nil otherButton:@"Cancel"
informativeTextWithFormat:@"Do you want to create a new site named:\n\n%@", siteName];
[alert beginSheetModalForWindow:self.window modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:MPAlertCreateSite];
}];
}
@end

View File

@@ -63,6 +63,14 @@
<reference key="NSSuperview" ref="1072816887"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="402376051"/>
<object class="NSShadow" key="NSViewShadow">
<double key="NSShadowVert">1</double>
<double key="NSShadowBlurRadius">1</double>
<object class="NSColor" key="NSShadowColor" id="546806116">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MCAwLjYAA</bytes>
</object>
</object>
<string key="NSReuseIdentifierKey">_NS:1535</string>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="406294418">
@@ -99,6 +107,14 @@
<reference key="NSSuperview" ref="1072816887"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="104294954"/>
<object class="NSShadow" key="NSViewShadow">
<double key="NSShadowVert">1</double>
<double key="NSShadowBlurRadius">1</double>
<object class="NSColor" key="NSShadowColor" id="227321360">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MCAwLjYAA</bytes>
</object>
</object>
<string key="NSReuseIdentifierKey">_NS:1505</string>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="249851874">
@@ -173,7 +189,6 @@
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="49669222"/>
<object class="NSShadow" key="NSViewShadow">
<double key="NSShadowHoriz">1</double>
<double key="NSShadowVert">1</double>
<double key="NSShadowBlurRadius">1</double>
<object class="NSColor" key="NSShadowColor" id="444840817">
@@ -181,6 +196,17 @@
<bytes key="NSWhite">MCAwLjYAA</bytes>
</object>
</object>
<array key="NSViewContentFilters">
<object class="CIGaussianBlur">
<real value="0.40000000000000002" key="CI_inputRadius"/>
<bool key="CIEnabled">YES</bool>
</object>
<object class="CIBloom">
<real value="10" key="CI_inputRadius"/>
<real value="0.5" key="CI_inputIntensity"/>
<bool key="CIEnabled">YES</bool>
</object>
</array>
<string key="NSReuseIdentifierKey">_NS:9</string>
<string key="NSAntiCompressionPriority">{250, 750}</string>
<bool key="NSEnabled">YES</bool>
@@ -218,6 +244,7 @@
<string key="NSFrame">{{224, 84}, {32, 32}}</string>
<reference key="NSSuperview" ref="258451033"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:945</string>
<int key="NSpiFlags">28682</int>
<double key="NSMaxValue">100</double>
@@ -833,7 +860,7 @@
<real value="1" key="181.IBViewIntegration.shadowBlurRadius"/>
<reference key="181.IBViewIntegration.shadowColor" ref="444840817"/>
<real value="1" key="181.IBViewIntegration.shadowOffsetHeight"/>
<real value="1" key="181.IBViewIntegration.shadowOffsetWidth"/>
<real value="0.0" key="181.IBViewIntegration.shadowOffsetWidth"/>
<array class="NSMutableArray" key="182.IBNSViewMetadataConstraints">
<reference ref="102475933"/>
</array>
@@ -845,6 +872,10 @@
<real value="0.0" key="182.IBViewIntegration.shadowOffsetWidth"/>
<boolean value="NO" key="183.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="183.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<real value="1" key="183.IBViewIntegration.shadowBlurRadius"/>
<reference key="183.IBViewIntegration.shadowColor" ref="227321360"/>
<real value="1" key="183.IBViewIntegration.shadowOffsetHeight"/>
<real value="0.0" key="183.IBViewIntegration.shadowOffsetWidth"/>
<string key="184.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="185.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="186.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -859,6 +890,10 @@
<string key="210.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="NO" key="216.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/>
<string key="216.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<real value="1" key="216.IBViewIntegration.shadowBlurRadius"/>
<reference key="216.IBViewIntegration.shadowColor" ref="546806116"/>
<real value="1" key="216.IBViewIntegration.shadowOffsetHeight"/>
<real value="0.0" key="216.IBViewIntegration.shadowOffsetWidth"/>
<string key="217.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="218.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="219.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>

View File

@@ -13,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>MasterPassword</string>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>

View File

@@ -27,6 +27,8 @@
DA3EF17B15A47744003ABF4E /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA3EF17A15A47744003ABF4E /* SenTestingKit.framework */; };
DA3EF17D15A47744003ABF4E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DA4425CC1557BED40052177D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DA4C45F4173B57B700745CC5 /* NSURL+UbiquityStoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DA4C45F2173B57B700745CC5 /* NSURL+UbiquityStoreManager.h */; };
DA4C45F5173B57B700745CC5 /* NSURL+UbiquityStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA4C45F3173B57B700745CC5 /* NSURL+UbiquityStoreManager.m */; };
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
DA5E5C8817248AA1003798D8 /* crypto_aesctr.h in Headers */ = {isa = PBXBuildFile; fileRef = DA5E5C7B17248AA1003798D8 /* crypto_aesctr.h */; };
DA5E5C8917248AA1003798D8 /* crypto_scrypt.h in Headers */ = {isa = PBXBuildFile; fileRef = DA5E5C7C17248AA1003798D8 /* crypto_scrypt.h */; };
@@ -201,7 +203,9 @@
DA3EF17915A47744003ABF4E /* Tests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
DA3EF17A15A47744003ABF4E /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libUbiquityStoreManager.a; sourceTree = BUILT_PRODUCTS_DIR; };
DA5BFA44147E415C00F98B1E /* MasterPassword-Mac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MasterPassword-Mac.app"; sourceTree = BUILT_PRODUCTS_DIR; };
DA4C45F2173B57B700745CC5 /* NSURL+UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+UbiquityStoreManager.h"; sourceTree = "<group>"; };
DA4C45F3173B57B700745CC5 /* NSURL+UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+UbiquityStoreManager.m"; sourceTree = "<group>"; };
DA5BFA44147E415C00F98B1E /* MasterPassword.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MasterPassword.app; sourceTree = BUILT_PRODUCTS_DIR; };
DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
DA5BFA4E147E415C00F98B1E /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
@@ -440,7 +444,7 @@
DA5BFA45147E415C00F98B1E /* Products */ = {
isa = PBXGroup;
children = (
DA5BFA44147E415C00F98B1E /* MasterPassword-Mac.app */,
DA5BFA44147E415C00F98B1E /* MasterPassword.app */,
DAC77CAD148291A600BCF976 /* libPearl.a */,
DAC6326C148680650075AEA5 /* libjrswizzle.a */,
DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */,
@@ -577,6 +581,8 @@
DACA22B61705DE7D002C6C22 /* UbiquityStoreManager */ = {
isa = PBXGroup;
children = (
DA4C45F2173B57B700745CC5 /* NSURL+UbiquityStoreManager.h */,
DA4C45F3173B57B700745CC5 /* NSURL+UbiquityStoreManager.m */,
DACA22BA1705DE7D002C6C22 /* UbiquityStoreManager.h */,
DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */,
DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */,
@@ -800,6 +806,7 @@
files = (
DACA22BC1705DE7D002C6C22 /* NSError+UbiquityStoreManager.h in Headers */,
DACA22BE1705DE7D002C6C22 /* UbiquityStoreManager.h in Headers */,
DA4C45F4173B57B700745CC5 /* NSURL+UbiquityStoreManager.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -893,9 +900,9 @@
productReference = DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */;
productType = "com.apple.product-type.library.static";
};
DA5BFA43147E415C00F98B1E /* MasterPassword-Mac */ = {
DA5BFA43147E415C00F98B1E /* MasterPassword */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword-Mac" */;
buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */;
buildPhases = (
DA5BFA40147E415C00F98B1E /* Sources */,
DA5BFA41147E415C00F98B1E /* Frameworks */,
@@ -907,9 +914,9 @@
);
dependencies = (
);
name = "MasterPassword-Mac";
name = MasterPassword;
productName = MasterPassword;
productReference = DA5BFA44147E415C00F98B1E /* MasterPassword-Mac.app */;
productReference = DA5BFA44147E415C00F98B1E /* MasterPassword.app */;
productType = "com.apple.product-type.application";
};
DAC6326B148680650075AEA5 /* jrswizzle */ = {
@@ -971,7 +978,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
DA5BFA43147E415C00F98B1E /* MasterPassword-Mac */,
DA5BFA43147E415C00F98B1E /* MasterPassword */,
DAC77CAC148291A600BCF976 /* Pearl */,
DAC6326B148680650075AEA5 /* jrswizzle */,
DA4425CA1557BED40052177D /* UbiquityStoreManager */,
@@ -1116,6 +1123,7 @@
files = (
DACA22BB1705DE7D002C6C22 /* UbiquityStoreManager.m in Sources */,
DACA22BD1705DE7D002C6C22 /* NSError+UbiquityStoreManager.m in Sources */,
DA4C45F5173B57B700745CC5 /* NSURL+UbiquityStoreManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1190,7 +1198,7 @@
/* Begin PBXTargetDependency section */
DA3EF19E15A47AEB003ABF4E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DA5BFA43147E415C00F98B1E /* MasterPassword-Mac */;
target = DA5BFA43147E415C00F98B1E /* MasterPassword */;
targetProxy = DA3EF19D15A47AEB003ABF4E /* PBXContainerItemProxy */;
};
DAC63286148681200075AEA5 /* PBXTargetDependency */ = {
@@ -1717,7 +1725,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "AdHoc-Mac";
};
DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword-Mac" */ = {
DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA5BFA6E147E415C00F98B1E /* Debug-Mac */,

View File

@@ -15,8 +15,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-Mac.app"
BlueprintName = "MasterPassword-Mac"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@@ -43,8 +43,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-Mac.app"
BlueprintName = "MasterPassword-Mac"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
@@ -61,8 +61,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-Mac.app"
BlueprintName = "MasterPassword-Mac"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>

View File

@@ -15,8 +15,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-Mac.app"
BlueprintName = "MasterPassword-Mac"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@@ -43,11 +43,18 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-Mac.app"
BlueprintName = "MasterPassword-Mac"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "CA_DEBUG_TRANSACTIONS"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
@@ -61,8 +68,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-Mac.app"
BlueprintName = "MasterPassword-Mac"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-Mac.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>

View File

@@ -201,6 +201,41 @@
</array>
</object>
</object>
<object class="NSMenuItem" id="939693094">
<reference key="NSMenu" ref="800575174"/>
<string key="NSTitle">Advanced</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
<string key="NSAction">submenuAction:</string>
<object class="NSMenu" key="NSSubmenu" id="534220172">
<string key="NSTitle">Advanced</string>
<array class="NSMutableArray" key="NSMenuItems">
<object class="NSMenuItem" id="842321178">
<reference key="NSMenu" ref="534220172"/>
<string key="NSTitle">iCloud Truth Sync</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
</object>
<object class="NSMenuItem" id="946018575">
<reference key="NSMenu" ref="534220172"/>
<bool key="NSIsDisabled">YES</bool>
<string key="NSTitle">Force this device's version of the truth upon all others.</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="269450960"/>
<reference key="NSMixedImage" ref="977440657"/>
<object class="NSAttributedString" key="NSAttributedTitle">
<string key="NSString">Force this device's version of the truth upon all others.</string>
<reference key="NSAttributes" ref="583461090"/>
</object>
</object>
</array>
</object>
</object>
</array>
<bool key="NSNoAutoenable">YES</bool>
</object>
@@ -288,14 +323,6 @@
</object>
<int key="connectionID">731</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">activate:</string>
<reference key="source" ref="976324537"/>
<reference key="destination" ref="846612332"/>
</object>
<int key="connectionID">736</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">useICloudItem</string>
@@ -408,6 +435,22 @@
</object>
<int key="connectionID">774</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">rebuildCloud:</string>
<reference key="source" ref="976324537"/>
<reference key="destination" ref="842321178"/>
</object>
<int key="connectionID">780</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showPasswordWindow</string>
<reference key="source" ref="976324537"/>
<reference key="destination" ref="846612332"/>
</object>
<int key="connectionID">781</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
@@ -503,6 +546,7 @@
<reference ref="110488020"/>
<reference ref="123831322"/>
<reference ref="123543264"/>
<reference ref="939693094"/>
</array>
<reference key="parent" ref="851296005"/>
</object>
@@ -590,6 +634,33 @@
<reference key="object" ref="117792016"/>
<reference key="parent" ref="293904698"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">776</int>
<reference key="object" ref="939693094"/>
<array class="NSMutableArray" key="children">
<reference ref="534220172"/>
</array>
<reference key="parent" ref="800575174"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">777</int>
<reference key="object" ref="534220172"/>
<array class="NSMutableArray" key="children">
<reference ref="842321178"/>
<reference ref="946018575"/>
</array>
<reference key="parent" ref="939693094"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">778</int>
<reference key="object" ref="842321178"/>
<reference key="parent" ref="534220172"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">779</int>
<reference key="object" ref="946018575"/>
<reference key="parent" ref="534220172"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
@@ -628,14 +699,158 @@
<string key="766.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="767.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="768.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="776.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="777.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="778.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="779.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">774</int>
<int key="maxID">781</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">MPAppDelegate_Shared</string>
<string key="superclassName">PearlAppDelegate</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/MPAppDelegate_Shared.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">MPMacAppDelegate</string>
<string key="superclassName">MPAppDelegate_Shared</string>
<dictionary class="NSMutableDictionary" key="actions">
<string key="activate:">id</string>
<string key="lock:">id</string>
<string key="newUser:">NSMenuItem</string>
<string key="rebuildCloud:">id</string>
<string key="togglePreference:">NSMenuItem</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="actionInfosByName">
<object class="IBActionInfo" key="activate:">
<string key="name">activate:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo" key="lock:">
<string key="name">lock:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo" key="newUser:">
<string key="name">newUser:</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBActionInfo" key="rebuildCloud:">
<string key="name">rebuildCloud:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo" key="togglePreference:">
<string key="name">togglePreference:</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
</dictionary>
<dictionary class="NSMutableDictionary" key="outlets">
<string key="createUserItem">NSMenuItem</string>
<string key="dialogStyleHUD">NSMenuItem</string>
<string key="dialogStyleRegular">NSMenuItem</string>
<string key="lockItem">NSMenuItem</string>
<string key="rememberPasswordItem">NSMenuItem</string>
<string key="savePasswordItem">NSMenuItem</string>
<string key="showItem">NSMenuItem</string>
<string key="statusMenu">NSMenu</string>
<string key="useICloudItem">NSMenuItem</string>
<string key="usersItem">NSMenuItem</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
<object class="IBToOneOutletInfo" key="createUserItem">
<string key="name">createUserItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="dialogStyleHUD">
<string key="name">dialogStyleHUD</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="dialogStyleRegular">
<string key="name">dialogStyleRegular</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="lockItem">
<string key="name">lockItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="rememberPasswordItem">
<string key="name">rememberPasswordItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="savePasswordItem">
<string key="name">savePasswordItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="showItem">
<string key="name">showItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="statusMenu">
<string key="name">statusMenu</string>
<string key="candidateClassName">NSMenu</string>
</object>
<object class="IBToOneOutletInfo" key="useICloudItem">
<string key="name">useICloudItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="usersItem">
<string key="name">usersItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
</dictionary>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/MPMacAppDelegate.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">PearlAppDelegate</string>
<string key="superclassName">UIResponder</string>
<dictionary class="NSMutableDictionary" key="outlets">
<string key="navigationController">UINavigationController</string>
<string key="window">UIWindow</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
<object class="IBToOneOutletInfo" key="navigationController">
<string key="name">navigationController</string>
<string key="candidateClassName">UINavigationController</string>
</object>
<object class="IBToOneOutletInfo" key="window">
<string key="name">window</string>
<string key="candidateClassName">UIWindow</string>
</object>
</dictionary>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/PearlAppDelegate.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UINavigationController</string>
<string key="superclassName">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/UINavigationController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIWindow</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/UIWindow.h</string>
</object>
</object>
</array>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">

View File

@@ -55,9 +55,11 @@
return;
__weak MPElementListAllViewController *wSelf = self;
[self addElementNamed:[alert textFieldAtIndex:0].text completion:^(BOOL success) {
if (success)
[[MPiOSAppDelegate get] addElementNamed:[alert textFieldAtIndex:0].text completion:^(MPElementEntity *element) {
if (element) {
[wSelf.delegate didSelectElement:element];
[wSelf close:nil];
}
}];
}
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonOkay, nil];

View File

@@ -15,7 +15,6 @@
@property(readonly) NSDateFormatter *dateFormatter;
- (void)updateData;
- (void)addElementNamed:(NSString *)siteName completion:(void (^)(BOOL success))completion;
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atTableIndexPath:(NSIndexPath *)indexPath;
- (void)customTableViewUpdates;

View File

@@ -15,7 +15,7 @@
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
[self updateData];
}];
@@ -23,44 +23,6 @@
[super viewDidLoad];
}
- (void)addElementNamed:(NSString *)siteName completion:(void (^)(BOOL success))completion {
if (![siteName length]) {
if (completion)
completion( false );
return;
}
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
assert(activeUser);
MPElementType type = activeUser.defaultType;
if (!type)
type = activeUser.defaultType = MPElementTypeGeneratedLong;
NSString *typeEntityClassName = [MPAlgorithmDefault classNameOfType:type];
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityClassName
inManagedObjectContext:moc];
element.name = siteName;
element.user = activeUser;
element.type = type;
element.lastUsed = [NSDate date];
element.version = MPAlgorithmDefaultVersion;
[moc saveToStore];
NSManagedObjectID *elementOID = [element objectID];
dispatch_async( dispatch_get_main_queue(), ^{
MPElementEntity *element_ = (MPElementEntity *)[[MPiOSAppDelegate managedObjectContextForThreadIfReady]
objectRegisteredForID:elementOID];
[self.delegate didSelectElement:element_];
if (completion)
completion( true );
} );
}];
}
- (NSFetchedResultsController *)fetchedResultsControllerByLastUsed {
if (!_fetchedResultsControllerByLastUsed) {
@@ -145,9 +107,9 @@
NSError *error;
self.fetchedResultsControllerByLastUsed.fetchRequest.predicate = predicate;
self.fetchedResultsControllerByUses.fetchRequest.predicate = predicate;
if (![self.fetchedResultsControllerByLastUsed performFetch:&error])
if (self.fetchedResultsControllerByLastUsed && ![self.fetchedResultsControllerByLastUsed performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
if (![self.fetchedResultsControllerByUses performFetch:&error])
if (self.fetchedResultsControllerByUses && ![self.fetchedResultsControllerByUses performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
[self.tableView reloadData];
@@ -157,50 +119,46 @@
}
// See MP-14, also crashes easily on internal assertions etc..
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
dbg(@"%@", NSStringFromSelector( _cmd ));
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
switch (type) {
case NSFetchedResultsChangeInsert:
dbg(@"%@ -- NSFetchedResultsChangeInsert:%@", NSStringFromSelector( _cmd ), anObject);
[self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
dbg(@"%@ -- NSFetchedResultsChangeDelete:%@", NSStringFromSelector( _cmd ), anObject);
[self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
dbg(@"%@ -- NSFetchedResultsChangeUpdate:%@", NSStringFromSelector( _cmd ), anObject);
[self.tableView reloadRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeMove:
dbg(@"%@ -- NSFetchedResultsChangeMove:%@", NSStringFromSelector( _cmd ), anObject);
[self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
//- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
//
// [self.tableView beginUpdates];
//}
//
//- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
// atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
//
// switch (type) {
//
// case NSFetchedResultsChangeInsert:
// [self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// break;
//
// case NSFetchedResultsChangeDelete:
// [self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// break;
//
// case NSFetchedResultsChangeUpdate:
// [self.tableView reloadRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// break;
//
// case NSFetchedResultsChangeMove:
// [self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// [self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// break;
// }
//}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
dbg(@"%@ on %@", NSStringFromSelector( _cmd ), [NSThread currentThread].name);
[self customTableViewUpdates];
[self.tableView endUpdates];
// [self customTableViewUpdates];
// [self.tableView endUpdates];
[self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
@@ -296,12 +254,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

@@ -9,6 +9,7 @@
#import "MPElementListSearchController.h"
#import "MPMainViewController.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
@interface MPElementListSearchController()
@@ -216,7 +217,11 @@
if (buttonIndex == [alert cancelButtonIndex])
return;
[self addElementNamed:siteName completion:nil];
__weak MPElementListController *wSelf = self;
[[MPiOSAppDelegate get] addElementNamed:siteName completion:^(MPElementEntity *element) {
if (element)
[wSelf.delegate didSelectElement:element];
}];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPLogsViewController.h
// MPLogsViewController
//
// Created by lhunath on 2013-04-29.
// Copyright, lhunath (Maarten Billemont) 2013. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface MPLogsViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITextView *logView;
@property (weak, nonatomic) IBOutlet UISegmentedControl *levelControl;
- (IBAction)toggleLevelControl:(UISegmentedControl *)sender;
- (IBAction)close:(UIBarButtonItem *)sender;
- (IBAction)refresh:(UIBarButtonItem *)sender;
- (IBAction)mail:(UIBarButtonItem *)sender;
@end

View File

@@ -0,0 +1,87 @@
/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPLogsViewController.h
// MPLogsViewController
//
// Created by lhunath on 2013-04-29.
// Copyright, lhunath (Maarten Billemont) 2013. All rights reserved.
//
#import "MPLogsViewController.h"
#import "MPiOSAppDelegate.h"
@implementation MPLogsViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserverForName:NSUserDefaultsDidChangeNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
self.levelControl.selectedSegmentIndex = [[MPiOSConfig get].traceMode boolValue]? 1: 0;
}];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self refresh:nil];
self.levelControl.selectedSegmentIndex = [[MPiOSConfig get].traceMode boolValue]? 1: 0;
}
- (IBAction)toggleLevelControl:(UISegmentedControl *)sender {
BOOL traceEnabled = (BOOL)self.levelControl.selectedSegmentIndex;
if (traceEnabled) {
[PearlAlert showAlertWithTitle:@"Enable Trace Mode?" message:
@"Trace mode will log the internal operation of the application.\n"
@"Unless you're looking for the cause of a problem, you should leave this off to save memory."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[MPiOSConfig get].traceMode = @YES;
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Enable Trace", nil];
}
else
[MPiOSConfig get].traceMode = @NO;
}
- (IBAction)close:(UIBarButtonItem *)sender {
[self.navigationController popViewControllerAnimated:YES];
}
- (IBAction)refresh:(UIBarButtonItem *)sender {
self.logView.text = [[PearlLogger get] formatMessagesWithLevel:PearlLogLevelTrace];
}
- (IBAction)mail:(UIBarButtonItem *)sender {
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

@@ -107,7 +107,7 @@
[self.navigationController popToRootViewControllerAnimated:animated];
}];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
if (!self.activeElementForThread)
[self didSelectElement:nil];
@@ -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,8 +596,10 @@
return;
NSString *oldPassword = [activeElement.content description];
if (!task( activeElement ))
if (!task( activeElement, context ))
return;
activeElement = [self activeElementInContext:context];
NSString *newPassword = [activeElement.content description];
// Save.
@@ -669,7 +672,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 +788,16 @@
@"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) {
if ([activeElement.algorithm classOfType:type] != activeElement.typeClass) {
do:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) {
if ([activeElement.algorithm classOfType:type] == activeElement.typeClass)
activeElement.type = type;
else {
// Type requires a different class of element. Recreate the element.
MPElementEntity *newElement
= [NSEntityDescription insertNewObjectForEntityForName:[activeElement.algorithm classNameOfType:type]
inManagedObjectContext:activeElement.managedObjectContext];
inManagedObjectContext:context];
newElement.type = type;
newElement.name = activeElement.name;
newElement.user = activeElement.user;
newElement.uses = activeElement.uses;
@@ -798,11 +805,16 @@
newElement.version = activeElement.version;
newElement.loginName = activeElement.loginName;
[activeElement.managedObjectContext deleteObject:activeElement];
[context deleteObject:activeElement];
[context saveToStore];
NSError *error;
if (![context obtainPermanentIDsForObjects:@[ newElement ] error:&error])
err(@"Failed to obtain a permanent object ID after changing object type: %@", error);
_activeElementOID = newElement.objectID;
activeElement = newElement;
}
activeElement.type = type;
[[NSNotificationCenter defaultCenter]
postNotificationName:MPElementUpdatedNotification object:activeElement.objectID];
@@ -817,7 +829,7 @@
[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"
@@ -843,16 +855,16 @@
} );
}
}];
MPCheckpoint( MPCheckpointUseType, @{
@"type" : element.typeName,
@"version" : @(element.version)
} );
}
[self.searchDisplayController setActive:NO animated:YES];
self.searchDisplayController.searchBar.text = element.name;
MPCheckpoint( MPCheckpointUseType, @{
@"type" : element.typeName,
@"version" : @(element.version)
} );
[self updateAnimated:YES];
}
@@ -880,7 +892,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;
}];
@@ -893,7 +905,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);
@@ -89,6 +93,21 @@
[super viewWillDisappear:animated];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
MPCheckpoint( MPCheckpointLogs, @{
@"trace": [MPiOSConfig get].traceMode
} );
[self performSegueWithIdentifier:@"MP_Logs" sender:self];
}
}
- (BOOL)shouldAutorotate {
return NO;
@@ -114,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];
@@ -125,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];
}
@@ -141,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

@@ -77,7 +77,9 @@
[(UITextField *)[cell viewWithTag:2] setText:@"..."];
NSString *name = selectedElement.name;
NSUInteger counter = ((MPElementGeneratedEntity *)selectedElement).counter;
NSUInteger counter = 0;
if ([selectedElement isKindOfClass:[MPElementGeneratedEntity class]])
counter = ((MPElementGeneratedEntity *)selectedElement).counter;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
NSString *typeContent = [MPAlgorithmDefault generateContentNamed:name ofType:cellType

View File

@@ -8,12 +8,14 @@
#import <QuartzCore/QuartzCore.h>
#import <Social/Social.h>
#import <CoreGraphics/CoreGraphics.h>
#import "MPUnlockViewController.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
@interface MPUnlockViewController()
@property(strong, nonatomic) NSMutableDictionary *avatarToUserOID;
@@ -28,6 +30,7 @@
@property(nonatomic, strong) NSArray *marqueeTipTexts;
@end
@implementation MPUnlockViewController {
NSManagedObjectID *_selectedUserOID;
}
@@ -116,7 +119,8 @@
- (void)viewDidLoad {
[self.newsView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.masterpasswordapp.com/news.html"]]];
NSString *newsURL = PearlString( @"http://www.masterpasswordapp.com/news.html?version=%@", [[PearlInfoPlist get] CFBundleVersion] );
[self.newsView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:newsURL]]];
self.avatarToUserOID = [NSMutableDictionary dictionaryWithCapacity:3];
@@ -161,11 +165,11 @@
[self initializeWordLabel:wordLabel];
} recurse:NO];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
[self updateUsers];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidImportChangesNotification object:nil queue:nil
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidImportChangesNotification object:nil queue:nil
usingBlock:^(NSNotification *note) {
[self updateUsers];
}];
@@ -206,6 +210,8 @@
- (void)viewDidAppear:(BOOL)animated {
[self becomeFirstResponder];
if (!animated && !self.navigationController.presentedViewController)
[[self findTargetedAvatar] setSelected:YES];
else
@@ -251,10 +257,21 @@
self.emergencyGeneratorContainer.alpha = 0;
self.emergencyGeneratorContainer.hidden = NO;
[UIView animateWithDuration:0.5 animations:^{
self.emergencyGeneratorContainer.frame = CGRectSetX( self.emergencyGeneratorContainer.frame,
self.emergencyGeneratorContainer.frame.origin.x - 100 );
[UIView animateWithDuration:0.3 animations:^{
self.emergencyGeneratorContainer.frame = CGRectSetX( self.emergencyGeneratorContainer.frame,
self.emergencyGeneratorContainer.frame.origin.x + 150 );
self.emergencyGeneratorContainer.alpha = 1;
} completion:^(BOOL finished) {
if (!finished)
return;
[self.emergencyName becomeFirstResponder];
[UIView animateWithDuration:0.2 animations:^{
self.emergencyGeneratorContainer.frame = CGRectSetX( self.emergencyGeneratorContainer.frame,
self.emergencyGeneratorContainer.frame.origin.x - 50 );
}];
}];
}
}
@@ -344,20 +361,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;
}
@@ -370,15 +386,17 @@
- (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
inManagedObjectContext:moc];
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity
*newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
inManagedObjectContext:moc];
[self showNewUserNameAlertFor:newUser inContext:moc completion:^(BOOL finished) {
newUserAvatar.selected = NO;
self.selectedUser = newUser;
}];
}];
}])
newUserAvatar.selected = NO;
}
- (void)showNewUserNameAlertFor:(MPUserEntity *)newUser inContext:(NSManagedObjectContext *)moc
@@ -449,7 +467,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];
@@ -481,7 +504,7 @@
if (selectedUser && !self.passwordView.alpha) {
// User was just selected.
self.passwordView.alpha = 1;
self.avatarsView.center = CGPointMake( 160, 180 );
self.avatarsView.frame = CGRectSetY( self.avatarsView.frame, 16 );
self.avatarsView.scrollEnabled = NO;
self.nameLabel.center = CGPointMake( 160, 94 );
self.nameLabel.backgroundColor = [UIColor blackColor];
@@ -492,7 +515,7 @@
// User was just deselected.
self.passwordField.text = nil;
self.passwordView.alpha = 0;
self.avatarsView.center = CGPointMake( 160, 310 );
self.avatarsView.frame = CGRectSetY( self.avatarsView.frame, 140 );
self.avatarsView.scrollEnabled = YES;
self.nameLabel.center = CGPointMake( 160, 296 );
self.nameLabel.backgroundColor = [UIColor clearColor];
@@ -534,7 +557,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) {
@@ -552,7 +575,8 @@
} recurse:NO];
if (allowScroll) {
CGPoint targetContentOffset = CGPointMake( MAX(0, targetedAvatar.center.x - self.avatarsView.bounds.size.width / 2),
CGPoint targetContentOffset = CGPointMake(
MAX(0, targetedAvatar.center.x - self.avatarsView.bounds.size.width / 2),
self.avatarsView.contentOffset.y );
if (!CGPointEqualToPoint( self.avatarsView.contentOffset, targetContentOffset ))
[self.avatarsView setContentOffset:targetContentOffset animated:animated];
@@ -618,7 +642,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(), ^{
@@ -655,11 +679,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)
@@ -919,7 +939,7 @@
[[self.emergencyGeneratorContainer findFirstResponderInHierarchy] resignFirstResponder];
if (animated) {
[UIView animateWithDuration:0.5 animations:^{
[UIView animateWithDuration:0.2 animations:^{
self.emergencyGeneratorContainer.alpha = 0;
} completion:^(BOOL finished) {
[self emergencyCloseAnimated:NO];
@@ -983,11 +1003,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) {
@@ -1007,7 +1027,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];
} );
@@ -1138,6 +1158,11 @@
- (void)setSelectedUser:(MPUserEntity *)selectedUser {
NSError *error = nil;
if (selectedUser.objectID.isTemporaryID &&
![selectedUser.managedObjectContext obtainPermanentIDsForObjects:@[ selectedUser ] error:&error])
err(@"Failed to obtain a permanent object ID after setting selected user: %@", error);
_selectedUserOID = selectedUser.objectID;
}

View File

@@ -17,8 +17,9 @@
- (void)showGuide;
- (void)showSetup;
- (void)showFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController;
- (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

@@ -12,15 +12,25 @@
#import "IASKSettingsReader.h"
#import "GPPSignIn.h"
@interface MPiOSAppDelegate()
@property(nonatomic, strong) PearlAlert *handleCloudContentAlert;
@property(nonatomic, strong) PearlAlert *fixCloudContentAlert;
@property(nonatomic, strong) PearlOverlay *storeLoading;
@end
@implementation MPiOSAppDelegate
+ (void)initialize {
[MPiOSConfig get];
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
#ifdef DEBUG
[PearlLogger get].printLevel = PearlLogLevelDebug;
//[NSClassFromString(@"WebView") performSelector:NSSelectorFromString(@"_enableRemoteInspector")];
#else
if ([[MPiOSConfig get].sendInfo boolValue])
[PearlLogger get].printLevel = PearlLogLevelInfo;
#endif
}
@@ -202,23 +212,28 @@
[[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
#ifdef TESTFLIGHT_SDK_VERSION
[TestFlight addCustomEnvironmentInformation:[@([[MPConfig get].rememberLogin boolValue]) description]
[TestFlight addCustomEnvironmentInformation:PearlStringNSB([MPConfig get].rememberLogin)
forKey:@"rememberLogin"];
[TestFlight addCustomEnvironmentInformation:[@([self storeManager].cloudEnabled) description] forKey:@"iCloud"];
[TestFlight addCustomEnvironmentInformation:[@([[MPConfig get].iCloudDecided boolValue]) description]
[TestFlight addCustomEnvironmentInformation:PearlStringB([self storeManager].cloudEnabled)
forKey:@"iCloud"];
[TestFlight addCustomEnvironmentInformation:PearlStringNSB([MPConfig get].iCloudDecided)
forKey:@"iCloudDecided"];
[TestFlight addCustomEnvironmentInformation:[@([[MPiOSConfig get].sendInfo boolValue]) description] forKey:@"sendInfo"];
[TestFlight addCustomEnvironmentInformation:[@([[MPiOSConfig get].helpHidden boolValue]) description]
[TestFlight addCustomEnvironmentInformation:PearlStringNSB([MPiOSConfig get].sendInfo)
forKey:@"sendInfo"];
[TestFlight addCustomEnvironmentInformation:PearlStringNSB([MPiOSConfig get].helpHidden)
forKey:@"helpHidden"];
[TestFlight addCustomEnvironmentInformation:[@([[MPiOSConfig get].showSetup boolValue]) description]
[TestFlight addCustomEnvironmentInformation:PearlStringNSB([MPiOSConfig get].showSetup)
forKey:@"showQuickStart"];
[TestFlight addCustomEnvironmentInformation:[@([[PearlConfig get].firstRun boolValue]) description] forKey:@"firstRun"];
[TestFlight addCustomEnvironmentInformation:[[PearlConfig get].launchCount description] forKey:@"launchCount"];
[TestFlight addCustomEnvironmentInformation:[@([[PearlConfig get].askForReviews boolValue]) description]
[TestFlight addCustomEnvironmentInformation:PearlStringNSB([PearlConfig get].firstRun)
forKey:@"firstRun"];
[TestFlight addCustomEnvironmentInformation:PearlStringNSB([PearlConfig get].launchCount)
forKey:@"launchCount"];
[TestFlight addCustomEnvironmentInformation:PearlStringNSB([PearlConfig get].askForReviews)
forKey:@"askForReviews"];
[TestFlight addCustomEnvironmentInformation:[[PearlConfig get].reviewAfterLaunches description]
[TestFlight addCustomEnvironmentInformation:PearlStringNSB([PearlConfig get].reviewAfterLaunches)
forKey:@"reviewAfterLaunches"];
[TestFlight addCustomEnvironmentInformation:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
[TestFlight addCustomEnvironmentInformation:[PearlConfig get].reviewedVersion
forKey:@"reviewedVersion"];
#endif
MPCheckpoint( MPCheckpointConfig, @{
@"rememberLogin" : @([[MPConfig get].rememberLogin boolValue]),
@@ -228,9 +243,9 @@
@"helpHidden" : @([[MPiOSConfig get].helpHidden boolValue]),
@"showQuickStart" : @([[MPiOSConfig get].showSetup boolValue]),
@"firstRun" : @([[PearlConfig get].firstRun boolValue]),
@"launchCount" : NilToNSNull([[PearlConfig get].launchCount description]),
@"launchCount" : NilToNSNull([PearlConfig get].launchCount),
@"askForReviews" : @([[PearlConfig get].askForReviews boolValue]),
@"reviewAfterLaunches" : NilToNSNull([[PearlConfig get].reviewAfterLaunches description]),
@"reviewAfterLaunches" : NilToNSNull([PearlConfig get].reviewAfterLaunches),
@"reviewedVersion" : NilToNSNull([PearlConfig get].reviewedVersion)
} );
}
@@ -261,6 +276,18 @@
[[MPiOSAppDelegate get] showSetup];
} );
MPCheckpoint(MPCheckpointStarted, @{
@"simulator" : PearlStringB([PearlDeviceUtils isSimulator]),
@"encrypted" : PearlStringB([PearlDeviceUtils isAppEncrypted]),
@"jailbroken" : PearlStringB([PearlDeviceUtils isJailbroken]),
@"platform" : [PearlDeviceUtils platform],
#ifdef APPSTORE
@"legal" : PearlStringB([PearlDeviceUtils isAppEncrypted]),
#else
@"legal" : @"YES",
#endif
});
return YES;
}
@@ -434,11 +461,6 @@
MPCheckpoint( MPCheckpointShowSetup, nil );
}
- (void)showFeedback {
[self showFeedbackWithLogs:NO forVC:nil];
}
- (void)showReview {
MPCheckpoint( MPCheckpointReview, nil );
@@ -475,7 +497,9 @@
- (void)openFeedbackWithLogs:(BOOL)logs forVC:(UIViewController *)viewController {
NSString *userName = [[MPiOSAppDelegate get] activeUserForThread].name;
PearlLogLevel logLevel = [[MPiOSConfig get].sendInfo boolValue]? PearlLogLevelDebug: PearlLogLevelInfo;
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 [%@]",
@@ -570,7 +594,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:
@@ -603,11 +627,77 @@
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value {
if (configKey == @selector(traceMode)) {
[PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo;
inf(@"Trace is now: %@", [[MPiOSConfig get].traceMode boolValue]? @"ON": @"OFF");
}
[[NSNotificationCenter defaultCenter]
postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey ) userInfo:nil];
}
#pragma mark - Google+
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore {
dispatch_async( dispatch_get_main_queue(), ^{
[self.handleCloudContentAlert cancelAlertAnimated:YES];
if (![self.storeLoading isVisible])
self.storeLoading = [PearlOverlay showOverlayWithTitle:@"Opening Your Data"];
} );
[super ubiquityStoreManager:manager willLoadStoreIsCloud:isCloudStore];
}
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didLoadStoreForCoordinator:(NSPersistentStoreCoordinator *)coordinator
isCloud:(BOOL)isCloudStore {
[super ubiquityStoreManager:manager didLoadStoreForCoordinator:coordinator isCloud:isCloudStore];
dispatch_async( dispatch_get_main_queue(), ^{
[self.handleCloudContentAlert cancelAlertAnimated:YES];
[self.fixCloudContentAlert cancelAlertAnimated:YES];
[self.storeLoading cancelOverlayAnimated:YES];
} );
}
- (BOOL)ubiquityStoreManager:(UbiquityStoreManager *)manager handleCloudContentCorruptionWithHealthyStore:(BOOL)storeHealthy {
if (manager.cloudEnabled && !storeHealthy && !([self.handleCloudContentAlert.alertView isVisible] || [self.fixCloudContentAlert.alertView isVisible]))
dispatch_async( dispatch_get_main_queue(), ^{
[self.storeLoading cancelOverlayAnimated:YES];
[self showCloudContentAlert];
} );
return NO;
}
- (void)showCloudContentAlert {
__weak MPiOSAppDelegate *wSelf = self;
[self.handleCloudContentAlert cancelAlertAnimated:NO];
self.handleCloudContentAlert = [PearlAlert showActivityWithTitle:@"iCloud Sync Problem" message:
@"Waiting for your other device to autocorrect the problem..."
initAlert:^(UIAlertView *alert) {
[alert addButtonWithTitle:@"Fix Now"];
}];
self.handleCloudContentAlert.tappedButtonBlock = ^(UIAlertView *alert, NSInteger buttonIndex) {
wSelf.fixCloudContentAlert = [PearlAlert showAlertWithTitle:@"Fix iCloud Now" message:
@"This problem can usually be autocorrected by opening the app on another device where you recently made changes.\n"
@"You can correct the problem from this device anyway, but recent changes made on another device might get lost."
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:
^(UIAlertView *alert_, NSInteger buttonIndex_) {
if (buttonIndex_ == alert_.cancelButtonIndex)
[wSelf showCloudContentAlert];
if (buttonIndex_ == [alert_ firstOtherButtonIndex])
[wSelf.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:YES];
}
cancelTitle:[PearlStrings get].commonButtonBack otherTitles:@"Fix Anyway", nil];
};
}
#pragma mark - Google+
- (NSDictionary *)googlePlusInfo {

View File

@@ -16,5 +16,6 @@
@property(nonatomic, retain) NSNumber *actionsTipShown;
@property(nonatomic, retain) NSNumber *typeTipShown;
@property(nonatomic, retain) NSNumber *loginNameTipShown;
@property(nonatomic, retain) NSNumber *traceMode;
@end

View File

@@ -8,7 +8,7 @@
@implementation MPiOSConfig
@dynamic sendInfo, helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown;
@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode;
- (id)init {
@@ -20,9 +20,10 @@
NSStringFromSelector( @selector(siteInfoHidden) ) : @YES,
NSStringFromSelector( @selector(showSetup) ) : @YES,
NSStringFromSelector( @selector(iTunesID) ) : @"510296984",
NSStringFromSelector( @selector(actionsTipShown) ) : PearlBoolNot(self.firstRun),
NSStringFromSelector( @selector(typeTipShown) ) : PearlBoolNot(self.firstRun),
NSStringFromSelector( @selector(loginNameTipShown) ) : PearlBool(NO)
NSStringFromSelector( @selector(actionsTipShown) ) : @(!self.firstRun),
NSStringFromSelector( @selector(typeTipShown) ) : @(!self.firstRun),
NSStringFromSelector( @selector(loginNameTipShown) ) : @NO,
NSStringFromSelector( @selector(traceMode) ) : @NO
}];
return self;

View File

@@ -9,7 +9,7 @@
<objects>
<tableViewController id="NKe-nv-566" customClass="MPTypeViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="btl-G4-V0S">
<rect key="frame" x="0.0" y="64" width="320" height="416"/>
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="calibratedRGB"/>
<view key="tableFooterView" contentMode="scaleToFill" id="aNa-wb-cYK">
@@ -476,16 +476,109 @@ Your passwords will be AES-encrypted with your master password.</string>
</objects>
<point key="canvasLocation" x="1537" y="145"/>
</scene>
<!--Logs View Controller - Log Inspector-->
<scene sceneID="AKo-Ze-vcJ">
<objects>
<viewController id="Tx0-mM-kHk" customClass="MPLogsViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="3oc-v8-YGP">
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" id="mtJ-9r-6yT">
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" id="ojc-Tn-DM1">
<rect key="frame" x="0.0" y="0.0" width="320" height="460"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<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"/>
</textView>
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="QPO-l8-Opz">
<rect key="frame" x="0.0" y="460" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<items>
<barButtonItem systemItem="compose" id="Yvq-If-VqG">
<connections>
<action selector="mail:" destination="Tx0-mM-kHk" id="Efg-Nz-tf2"/>
</connections>
</barButtonItem>
<barButtonItem systemItem="refresh" id="fzf-tt-Vc3">
<connections>
<action selector="refresh:" destination="Tx0-mM-kHk" id="Xur-Ed-PN7"/>
</connections>
</barButtonItem>
<barButtonItem systemItem="flexibleSpace" id="GcH-O1-v3J"/>
<barButtonItem style="plain" id="yi1-I8-uzn">
<segmentedControl key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="0" id="lzO-Kl-IPf">
<rect key="frame" x="191" y="8" width="123" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="Normal"/>
<segment title="Debug"/>
</segments>
<color key="tintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<action selector="toggleLevelControl:" destination="Tx0-mM-kHk" eventType="valueChanged" id="Snn-NE-BnD"/>
</connections>
</segmentedControl>
<color key="tintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
</barButtonItem>
</items>
</toolbar>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" title="Log Inspector" id="RrC-3Q-nBN">
<barButtonItem key="rightBarButtonItem" systemItem="done" id="RYe-xc-zYv">
<connections>
<action selector="close:" destination="Tx0-mM-kHk" id="KrS-tQ-IOf"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="levelControl" destination="lzO-Kl-IPf" id="B7k-yM-dR0"/>
<outlet property="logView" destination="ojc-Tn-DM1" id="mOS-LG-HHA"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="2Jl-Qq-bOz" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2078" y="145"/>
</scene>
<!--Main View Controller - Master Password-->
<scene sceneID="U26-Zf-euQ">
<objects>
<viewController id="PQa-Xl-A3x" customClass="MPMainViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ep0-Hn-5TR">
<rect key="frame" x="0.0" y="64" width="320" height="416"/>
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="center" image="background.png" id="0hY-LL-ITu">
<rect key="frame" x="0.0" y="44" width="320" height="372"/>
<rect key="frame" x="0.0" y="44" width="320" height="460"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<view contentMode="scaleToFill" id="Juh-jm-UDr" userLabel="View - Content">
@@ -665,7 +758,7 @@ Your passwords will be AES-encrypted with your master password.</string>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<view contentMode="scaleToFill" id="61G-By-qLB" userLabel="View - Help">
<rect key="frame" x="0.0" y="246" width="320" height="170"/>
<rect key="frame" x="0.0" y="334" width="320" height="170"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="top" image="Square-bottom.png" id="lbm-A0-3PK">
@@ -828,7 +921,7 @@ Your passwords will be AES-encrypted with your master password.</string>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<view hidden="YES" alpha="0.0" contentMode="scaleToFill" id="6wk-NU-VQG" userLabel="View - Outdated Alert">
<rect key="frame" x="10" y="230" width="300" height="180"/>
<rect key="frame" x="10" y="318" width="300" height="180"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<imageView contentMode="scaleToFill" image="tip_alert_black.png" id="f30-i7-VBv">
@@ -906,7 +999,7 @@ These sites should be upgraded and their account's passwords updated as soon as
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<view hidden="YES" alpha="0.0" contentMode="scaleToFill" id="yRY-qt-gz8" userLabel="View - Alert">
<rect key="frame" x="10" y="230" width="300" height="180"/>
<rect key="frame" x="10" y="318" width="300" height="180"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_alert_black.png" id="TUv-Tl-xTc">
@@ -1085,20 +1178,20 @@ L4m3P4sSw0rD</string>
<objects>
<viewController storyboardIdentifier="MPUnlockViewController" wantsFullScreenLayout="YES" modalTransitionStyle="flipHorizontal" id="Nbn-Rv-sP1" customClass="MPUnlockViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="GeE-5J-ZZO">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" id="QWe-Gw-rD3">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<view contentMode="scaleToFill" id="PHH-XC-9QQ" userLabel="View - UI Container">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_spinner.png" id="27q-lX-0vy">
<rect key="frame" x="105" y="40" width="110" height="110"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<rect key="frame" x="105" y="46" width="110" height="110"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<view contentMode="scaleToFill" id="JTj-nh-BWs" userLabel="Word Wall">
<rect key="frame" x="0.0" y="0.0" width="960" height="200"/>
@@ -1252,7 +1345,7 @@ L4m3P4sSw0rD</string>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="Tap and hold to delete or reset user." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="DBJ-Qi-ZcF">
<rect key="frame" x="20" y="460" width="280" height="20"/>
<rect key="frame" x="20" y="548" width="280" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="12"/>
<color key="textColor" cocoaTouchSystemColor="lightTextColor"/>
@@ -1444,7 +1537,7 @@ You could use the word wall for inspiration in finding a memorable master passw
</connections>
</button>
<webView opaque="NO" contentMode="scaleToFill" id="rGU-aZ-XVm">
<rect key="frame" x="0.0" y="411" width="320" height="44"/>
<rect key="frame" x="0.0" y="499" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<dataDetectorType key="dataDetectorTypes"/>
@@ -1456,15 +1549,15 @@ You could use the word wall for inspiration in finding a memorable master passw
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<view hidden="YES" alpha="0.30000000000000016" contentMode="scaleToFill" id="KNa-Xb-RuE" userLabel="View - Emergency Generator">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<rect key="frame" x="-100" y="0.0" width="520" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" id="vdf-Kn-Sle">
<rect key="frame" x="0.0" y="20" width="320" height="346"/>
<rect key="frame" x="100" y="20" width="320" height="434"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_panel_display.png" id="jLf-TI-anP">
<rect key="frame" x="11" y="0.0" width="298" height="346"/>
<rect key="frame" x="11" y="0.0" width="298" height="434"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<rect key="contentStretch" x="0.050000000000000003" y="0.10000000000000001" width="0.89999999999999991" height="0.79999999999999982"/>
</imageView>
@@ -1563,11 +1656,11 @@ You could use the word wall for inspiration in finding a memorable master passw
</connections>
</button>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" hidesWhenStopped="YES" style="whiteLarge" id="3Ax-91-gVM">
<rect key="frame" x="141.5" y="300" width="37" height="37"/>
<rect key="frame" x="141" y="388" width="37" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
</activityIndicatorView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="r9J-C9-Bt1">
<rect key="frame" x="20" y="292" width="280" height="54"/>
<rect key="frame" x="20" y="380" width="280" height="54"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" name="AmericanTypewriter-Bold" family="American Typewriter" pointSize="30"/>
<size key="titleShadowOffset" width="0.0" height="1"/>
@@ -1583,7 +1676,7 @@ You could use the word wall for inspiration in finding a memorable master passw
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" id="sD9-hR-UAI" userLabel="View - Content Tip">
<rect key="frame" x="55" y="266" width="210" height="60"/>
<rect key="frame" x="55" y="354" width="210" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" id="Lua-xm-WX6">
@@ -1679,11 +1772,11 @@ You could use the word wall for inspiration in finding a memorable master passw
<objects>
<viewController id="Q4b-t4-pwH" customClass="MPSetupViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="FnB-rI-puV">
<rect key="frame" x="0.0" y="64" width="320" height="416"/>
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" id="RjU-5e-mti">
<rect key="frame" x="0.0" y="-64" width="320" height="480"/>
<rect key="frame" x="0.0" y="-64" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="top" image="cloud.png" id="Ahr-aa-V1N">
@@ -1737,7 +1830,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<objects>
<tableViewController id="idA-Pj-1U9" customClass="MPElementListAllViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="48" sectionHeaderHeight="22" sectionFooterHeight="22" id="N83-sj-4tl">
<rect key="frame" x="0.0" y="20" width="320" height="460"/>
<rect key="frame" x="0.0" y="20" width="320" height="548"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<navigationBar key="tableHeaderView" contentMode="scaleToFill" id="l0p-Tu-L9k">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
@@ -1776,11 +1869,11 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<objects>
<viewController wantsFullScreenLayout="YES" id="2Th-Tb-22a" customClass="MPAppsViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="AhE-Ed-ajP">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" id="DHZ-5g-6vT">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<rect key="frame" x="0.0" y="44" width="320" height="480"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="book.png" id="wjL-OU-K7k">
@@ -1850,7 +1943,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<objects>
<tableViewController id="oLN-6u-GLb" customClass="MPPreferencesViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="oSh-Ap-kLt">
<rect key="frame" x="0.0" y="64" width="320" height="416"/>
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="calibratedRGB"/>
<view key="tableFooterView" contentMode="scaleToFill" id="63M-7L-M7o">
@@ -2172,6 +2265,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<outlet property="defaultTypeLabel" destination="vKJ-1b-NeO" id="4vz-2l-xXk"/>
<outlet property="exportCell" destination="X2m-92-Qzh" id="zjs-9C-uKX"/>
<outlet property="savePasswordSwitch" destination="ilG-0h-SOb" id="iZD-gQ-pve"/>
<segue destination="Tx0-mM-kHk" kind="push" identifier="MP_Logs" id="rWT-Kr-cAs"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CEl-jQ-l9k" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -2183,7 +2277,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<objects>
<tableViewController id="Vrp-Gl-7qn" customClass="IASKAppSettingsViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="singleLineEtched" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="NCp-ii-ux0">
<rect key="frame" x="0.0" y="64" width="320" height="416"/>
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<prototypes>
@@ -2267,11 +2361,11 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<objects>
<viewController id="ZgN-2j-05b" customClass="MPSetupViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="QIH-dS-sqD">
<rect key="frame" x="0.0" y="64" width="320" height="416"/>
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" id="FTf-06-1Pg">
<rect key="frame" x="0.0" y="-64" width="320" height="480"/>
<rect key="frame" x="0.0" y="-64" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Getting Started" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="FkR-cP-Y7K">
@@ -2282,7 +2376,7 @@ If you set a custom password, it will be encrypted before it is saved to the clo
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" usesAttributedText="YES" id="hwP-ds-GDh">
<rect key="frame" x="20" y="137" width="280" height="279"/>
<rect key="frame" x="20" y="137" width="280" height="367"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<attributedString key="attributedText">
@@ -2394,11 +2488,11 @@ You can make passwords for anything, like email addresses, sites or real-world t
<objects>
<viewController id="myN-X7-9Tg" customClass="MPGuideViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="vkG-Oi-PHo">
<rect key="frame" x="0.0" y="20" width="320" height="460"/>
<rect key="frame" x="0.0" y="20" width="320" height="548"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="center" image="background.png" id="C2i-R4-36i">
<rect key="frame" x="0.0" y="44" width="320" height="416"/>
<rect key="frame" x="0.0" y="44" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<view clipsSubviews="YES" contentMode="scaleToFill" id="vSk-nT-Vwf" userLabel="View - Display">
@@ -2538,7 +2632,7 @@ You can make passwords for anything, like email addresses, sites or real-world t
</scopeButtonTitles>
</searchBar>
<view alpha="0.60000000000000009" contentMode="scaleToFill" id="Ahl-o0-lMv">
<rect key="frame" x="0.0" y="0.0" width="320" height="460"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="548"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="3wI-lo-tWc">
@@ -2558,7 +2652,7 @@ You can make passwords for anything, like email addresses, sites or real-world t
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="UHf-cp-97W">
<rect key="frame" x="33" y="414" width="44" height="44"/>
<rect key="frame" x="33" y="502" width="44" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" hint="" label="Close"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
@@ -2574,7 +2668,7 @@ You can make passwords for anything, like email addresses, sites or real-world t
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="jDS-Vh-ETL">
<rect key="frame" x="124" y="190" width="72" height="80"/>
<rect key="frame" x="124" y="234" width="72" height="80"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" hint="" label="Close"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
@@ -2590,7 +2684,7 @@ You can make passwords for anything, like email addresses, sites or real-world t
</connections>
</button>
<progressView opaque="NO" contentMode="scaleToFill" id="nf7-oM-7dh">
<rect key="frame" x="85" y="431" width="150" height="9"/>
<rect key="frame" x="85" y="519" width="150" height="9"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="progressTintColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="calibratedRGB"/>
</progressView>
@@ -2729,11 +2823,11 @@ You can make passwords for anything, like email addresses, sites or real-world t
<objects>
<viewController id="kSj-yX-DmT" customClass="MPSetupViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="sT4-Jb-e5D">
<rect key="frame" x="0.0" y="64" width="320" height="416"/>
<rect key="frame" x="0.0" y="64" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" id="Eqt-R0-LTj">
<rect key="frame" x="0.0" y="-64" width="320" height="480"/>
<rect key="frame" x="0.0" y="-64" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="top" image="unlocked.png" id="4ah-P0-2DG">
@@ -2869,7 +2963,6 @@ However, it means that anyone who finds your device unlocked can do the same.</s
<relationships>
<relationship kind="action" name="close"/>
<relationship kind="action" name="play"/>
<relationship kind="action" name="toggleVolume"/>
<relationship kind="outlet" name="content" candidateClass="UIView"/>
<relationship kind="outlet" name="contentButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="contentText" candidateClass="UITextField"/>
@@ -2885,7 +2978,17 @@ However, it means that anyone who finds your device unlocked can do the same.</s
<relationship kind="outlet" name="typeTip" candidateClass="UIView"/>
<relationship kind="outlet" name="usernameButton" candidateClass="UIButton"/>
<relationship kind="outlet" name="usernameTip" candidateClass="UIView"/>
<relationship kind="outlet" name="volumeButton" candidateClass="UIButton"/>
</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">
@@ -3014,7 +3117,7 @@ However, it means that anyone who finds your device unlocked can do the same.</s
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<nil key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination"/>
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
<inferredMetricsTieBreakers>
<segue reference="jgo-j3-gbW"/>

View File

@@ -39,28 +39,19 @@
<string>Icon-72</string>
<string>Icon-Small</string>
<string>Icon-Small-50</string>
<string>Icon.png</string>
<string>Icon@2x.png</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>UINewsstandIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string></string>
</array>
<key>UINewsstandBindingEdge</key>
<string>UINewsstandBindingEdgeLeft</string>
<key>UINewsstandBindingType</key>
<string>UINewsstandBindingTypeMagazine</string>
</dict>
</dict>
<key>CFBundleIdentifier</key>
<string>com.lyndir.lhunath.MasterPassword</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>MasterPassword</string>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>

View File

@@ -16,10 +16,14 @@
93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3942A356B639724157982 /* PearlOverlay.h */; };
93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; };
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
93D399A3008C590EE104F856 /* NSURL+UbiquityStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3970B24FC0B53D989F7E3 /* NSURL+UbiquityStoreManager.m */; };
93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D391943675426839501BB8 /* MPLogsViewController.h */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
93D39EC9C559D2922B048CFE /* NSURL+UbiquityStoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3957F61D8AFE3B6580C32 /* NSURL+UbiquityStoreManager.h */; };
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A28369954D147E239BA /* MPSetupViewController.m */; };
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */; };
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */; };
DA30E9CE15722ECA00A68B4C /* NSBundle+PearlMutableInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9CB15722ECA00A68B4C /* NSBundle+PearlMutableInfo.h */; };
DA30E9CF15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9CC15722ECA00A68B4C /* NSBundle+PearlMutableInfo.m */; };
DA30E9D015722ECA00A68B4C /* Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9CD15722ECA00A68B4C /* Pearl.m */; };
@@ -968,13 +972,17 @@
/* Begin PBXFileReference section */
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = "<group>"; };
93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; };
93D394077F8FAB8167647187 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; };
93D3942A356B639724157982 /* PearlOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlOverlay.h; sourceTree = "<group>"; };
93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = "<group>"; };
93D3957F61D8AFE3B6580C32 /* NSURL+UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+UbiquityStoreManager.h"; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3970B24FC0B53D989F7E3 /* NSURL+UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+UbiquityStoreManager.m"; sourceTree = "<group>"; };
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSetupViewController.h; sourceTree = "<group>"; };
93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLogsViewController.m; sourceTree = "<group>"; };
93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; };
93D39A28369954D147E239BA /* MPSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSetupViewController.m; sourceTree = "<group>"; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
@@ -997,7 +1005,7 @@
DA5A09DE171A70E4005284AB /* play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play@2x.png"; sourceTree = "<group>"; };
DA5A09E8171BB0F7005284AB /* unlocked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unlocked.png; sourceTree = "<group>"; };
DA5A09E9171BB0F7005284AB /* unlocked@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "unlocked@2x.png"; sourceTree = "<group>"; };
DA5BFA44147E415C00F98B1E /* MasterPassword-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MasterPassword-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
DA5BFA44147E415C00F98B1E /* MasterPassword.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MasterPassword.app; sourceTree = BUILT_PRODUCTS_DIR; };
DA5BFA48147E415C00F98B1E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
@@ -2131,7 +2139,7 @@
DA5BFA45147E415C00F98B1E /* Products */ = {
isa = PBXGroup;
children = (
DA5BFA44147E415C00F98B1E /* MasterPassword-iOS.app */,
DA5BFA44147E415C00F98B1E /* MasterPassword.app */,
DAC77CAD148291A600BCF976 /* libPearl.a */,
DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */,
DAC6326C148680650075AEA5 /* libjrswizzle.a */,
@@ -3007,6 +3015,8 @@
DABD3BFC1711E2DC00CF925C /* main.m */,
93D39A28369954D147E239BA /* MPSetupViewController.m */,
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */,
93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */,
93D391943675426839501BB8 /* MPLogsViewController.h */,
);
path = iOS;
sourceTree = "<group>";
@@ -3066,6 +3076,8 @@
DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */,
DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */,
DACA22B91705DE7D002C6C22 /* NSError+UbiquityStoreManager.m */,
93D3970B24FC0B53D989F7E3 /* NSURL+UbiquityStoreManager.m */,
93D3957F61D8AFE3B6580C32 /* NSURL+UbiquityStoreManager.h */,
);
name = UbiquityStoreManager;
path = UbiquityStoreManager/UbiquityStoreManager;
@@ -3523,6 +3535,8 @@
files = (
DACA22BC1705DE7D002C6C22 /* NSError+UbiquityStoreManager.h in Headers */,
DACA22BE1705DE7D002C6C22 /* UbiquityStoreManager.h in Headers */,
93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */,
93D39EC9C559D2922B048CFE /* NSURL+UbiquityStoreManager.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3685,9 +3699,9 @@
productReference = DA497B9715E8C90E00B52167 /* libGoogle+.a */;
productType = "com.apple.product-type.library.static";
};
DA5BFA43147E415C00F98B1E /* MasterPassword-iOS */ = {
DA5BFA43147E415C00F98B1E /* MasterPassword */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword-iOS" */;
buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */;
buildPhases = (
DA5BFA40147E415C00F98B1E /* Sources */,
DA5BFA41147E415C00F98B1E /* Frameworks */,
@@ -3699,9 +3713,9 @@
);
dependencies = (
);
name = "MasterPassword-iOS";
name = MasterPassword;
productName = MasterPassword;
productReference = DA5BFA44147E415C00F98B1E /* MasterPassword-iOS.app */;
productReference = DA5BFA44147E415C00F98B1E /* MasterPassword.app */;
productType = "com.apple.product-type.application";
};
DA829E50159847E0002417D3 /* FontReplacer */ = {
@@ -3912,7 +3926,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
DA5BFA43147E415C00F98B1E /* MasterPassword-iOS */,
DA5BFA43147E415C00F98B1E /* MasterPassword */,
DAC77CAC148291A600BCF976 /* Pearl */,
DAC6325C1486805C0075AEA5 /* uicolor-utilities */,
DAC6326B148680650075AEA5 /* jrswizzle */,
@@ -4655,7 +4669,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = "/bin/bash -e";
shellScript = "PATH+=:/usr/libexec\n\naddPlistWithKey() {\n local key=$1 type=$2 value=$3 plist=${4:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Delete :'$key'\" \"$plist\" 2>/dev/null || true\n PlistBuddy -c \"Add :'$key' '$type' '$value'\" \"$plist\"\n}\nsetPlistWithKey() {\n local key=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Set :'$key' '$value'\" \"$plist\"\n}\ngetPlistWithKey() {\n local key=$1 plist=${2:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Print :'$key'\" \"$plist\"\n}\nsetSettingWithTitle() {\n local i title=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Settings.bundle/Root.plist\"}\n \n for (( i=0; 1; ++i )); do\n PlistBuddy -c \"Print :PreferenceSpecifiers:$i\" \"$plist\" &>/dev/null || break\n echo \"Checking preference specifier $i\"\n \n [[ $(PlistBuddy -c \"Print :PreferenceSpecifiers:$i:Title\" \"$plist\" 2>/dev/null) = $title ]] || continue\n \n echo \"Correct title, setting value.\"\n PlistBuddy -c \"Set :PreferenceSpecifiers:$i:DefaultValue $value\" \"$plist\"\n break\n done\n}\n\ndescription=$(git describe --always --dirty --long)\nbuild=${description%-g*} build=${build//-/.}\ntag=${description%%-*}\n\naddPlistWithKey GITDescription string \"$description\"\nsetPlistWithKey CFBundleVersion \"$build\"\nsetPlistWithKey CFBundleShortVersionString \"$tag\"\n\nsetSettingWithTitle \"Build\" \"$build\"\nsetSettingWithTitle \"Version\" \"$tag\"\nsetSettingWithTitle \"Copyright\" \"$(getPlistWithKey NSHumanReadableCopyright)\"\n";
shellScript = "PATH+=:/usr/libexec\n\naddPlistWithKey() {\n local key=$1 type=$2 value=$3 plist=${4:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Delete :'$key'\" \"$plist\" 2>/dev/null || true\n PlistBuddy -c \"Add :'$key' '$type' '$value'\" \"$plist\"\n}\nsetPlistWithKey() {\n local key=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Set :'$key' '$value'\" \"$plist\"\n}\ngetPlistWithKey() {\n local key=$1 plist=${2:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Print :'$key'\" \"$plist\"\n}\nsetSettingWithTitle() {\n local i title=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Settings.bundle/Root.plist\"}\n \n for (( i=0; 1; ++i )); do\n PlistBuddy -c \"Print :PreferenceSpecifiers:$i\" \"$plist\" &>/dev/null || break\n echo \"Checking preference specifier $i\"\n \n [[ $(PlistBuddy -c \"Print :PreferenceSpecifiers:$i:Title\" \"$plist\" 2>/dev/null) = $title ]] || continue\n \n echo \"Correct title, setting value.\"\n PlistBuddy -c \"Set :PreferenceSpecifiers:$i:DefaultValue $value\" \"$plist\"\n break\n done\n}\n\ndescription=$(git describe --always --dirty --long)\ncommit=${description##*-g} commit=$((0x$commit))\nversion=${description%-g*} version=${version//-/.}\n\naddPlistWithKey GITDescription string \"$description\"\nsetPlistWithKey CFBundleVersion \"$commit\"\nsetPlistWithKey CFBundleShortVersionString \"$version\"\n\nsetSettingWithTitle \"Build\" \"$commit\"\nsetSettingWithTitle \"Version\" \"$version\"\nsetSettingWithTitle \"Copyright\" \"$(getPlistWithKey NSHumanReadableCopyright)\"\n";
showEnvVarsInLog = 0;
};
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = {
@@ -4689,6 +4703,7 @@
files = (
DACA22BB1705DE7D002C6C22 /* UbiquityStoreManager.m in Sources */,
DACA22BD1705DE7D002C6C22 /* NSError+UbiquityStoreManager.m in Sources */,
93D399A3008C590EE104F856 /* NSURL+UbiquityStoreManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4771,6 +4786,7 @@
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */,
DABD3C271711E2DC00CF925C /* main.m in Sources */,
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */,
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4890,7 +4906,7 @@
/* Begin PBXTargetDependency section */
DA3EF19E15A47AEB003ABF4E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DA5BFA43147E415C00F98B1E /* MasterPassword-iOS */;
target = DA5BFA43147E415C00F98B1E /* MasterPassword */;
targetProxy = DA3EF19D15A47AEB003ABF4E /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
@@ -5084,7 +5100,7 @@
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
@@ -5154,7 +5170,7 @@
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
@@ -5279,7 +5295,7 @@
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
@@ -5563,7 +5579,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "AdHoc-iOS";
};
DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword-iOS" */ = {
DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA5BFA6E147E415C00F98B1E /* Debug-iOS */,
@@ -5631,6 +5647,7 @@
DAFC5661172C573B00CB5CC5 /* AppStore-iOS */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "AdHoc-iOS";
};
/* End XCConfigurationList section */

View File

@@ -15,8 +15,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-iOS.app"
BlueprintName = "MasterPassword-iOS"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@@ -43,8 +43,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-iOS.app"
BlueprintName = "MasterPassword-iOS"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
@@ -61,8 +61,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-iOS.app"
BlueprintName = "MasterPassword-iOS"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>

View File

@@ -15,8 +15,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-iOS.app"
BlueprintName = "MasterPassword-iOS"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@@ -33,8 +33,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-iOS.app"
BlueprintName = "MasterPassword-iOS"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
@@ -52,8 +52,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-iOS.app"
BlueprintName = "MasterPassword-iOS"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
@@ -70,8 +70,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
BuildableName = "MasterPassword-iOS.app"
BlueprintName = "MasterPassword-iOS"
BuildableName = "MasterPassword.app"
BlueprintName = "MasterPassword"
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>

2
Press

Submodule Press updated: 59d660c6de...88f1de4ef2