Migration cleanup and improvements.
[IMPROVED] Migration code cleanup. [FIXED] When local store migration fails, fall back to opening the old local store if possible. [FIXED] Better migration of cloud preferences. [FIXED] Better checking of when to perform cloud store migration. [FIXED] Always migrate the local store, even when the cloud store is enabled. [UPDATED] Removed the loading indicator in favor of a loading overlay. [UPDATED] Sorted the file references lexicographically.
This commit is contained in:
parent
418411769d
commit
036c04f65f
@ -9,6 +9,6 @@
|
|||||||
<inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="OCUnusedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="OCUnusedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="UnusedParameter" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
<inspection_tool class="UnusedParameter" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
2
External/Pearl
vendored
2
External/Pearl
vendored
@ -1 +1 @@
|
|||||||
Subproject commit ee6fff8dd4b5374c6b21c68392b5db1bf2819333
|
Subproject commit 17a3f485d1056976f55d0311d20ce4a693a62748
|
2
External/UbiquityStoreManager
vendored
2
External/UbiquityStoreManager
vendored
@ -1 +1 @@
|
|||||||
Subproject commit f2c303cbba0e0ce8b3949f30e54de9acc3e0a832
|
Subproject commit 2eea41bd7e75947db92b75d3f8a5e4360ee02e93
|
@ -9,6 +9,12 @@
|
|||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
|
||||||
|
#else
|
||||||
|
#define STORE_OPTIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
@implementation MPAppDelegate_Shared (Store)
|
@implementation MPAppDelegate_Shared (Store)
|
||||||
PearlAssociatedObjectProperty(PearlAlert*, HandleCloudContentAlert, handleCloudContentAlert);
|
PearlAssociatedObjectProperty(PearlAlert*, HandleCloudContentAlert, handleCloudContentAlert);
|
||||||
PearlAssociatedObjectProperty(PearlAlert*, FixCloudContentAlert, fixCloudContentAlert);
|
PearlAssociatedObjectProperty(PearlAlert*, FixCloudContentAlert, fixCloudContentAlert);
|
||||||
@ -73,165 +79,6 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
return self.privateManagedObjectContext;
|
return self.privateManagedObjectContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)migrateStoreForManager:(UbiquityStoreManager *)storeManager {
|
|
||||||
|
|
||||||
NSNumber *oldCloudEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"iCloudEnabledKey"];
|
|
||||||
dbg(@"iCloudEnabledKey: %@", oldCloudEnabled);
|
|
||||||
if (!oldCloudEnabled)
|
|
||||||
// No old data to migrate.
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ([oldCloudEnabled boolValue]) {
|
|
||||||
if (![storeManager cloudSafeForSeeding]) {
|
|
||||||
dbg(@"Cloud store already exists, refusing to migrate.");
|
|
||||||
// TODO: Make this final somehow by doing something with iCloudEnabledKey.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"];
|
|
||||||
NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"];
|
|
||||||
NSURL *newCloudStoreURL = [storeManager URLForCloudStore];
|
|
||||||
NSURL *newCloudContentURL = [storeManager 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"];
|
|
||||||
dbg(@"Migrating cloud:\n%@ ->\n%@", oldCloudStoreURL, newCloudStoreURL);
|
|
||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:oldCloudStoreURL.path isDirectory:NO]) {
|
|
||||||
// No old store to migrate from, cannot migrate.
|
|
||||||
wrn(@"Cannot migrate cloud store, old store not found at: %@", oldCloudStoreURL.path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
NSDictionary *oldCloudStoreOptions = @{
|
|
||||||
// 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,
|
|
||||||
NSPersistentStoreUbiquitousContentURLKey : oldCloudContentURL,
|
|
||||||
// So instead, we'll just open up the old store as read-only, if it exists.
|
|
||||||
NSReadOnlyPersistentStoreOption : @YES,
|
|
||||||
NSInferMappingModelAutomaticallyOption : @YES};
|
|
||||||
NSDictionary *newCloudStoreOptions = @{
|
|
||||||
NSPersistentStoreUbiquitousContentNameKey : [storeManager valueForKey:@"contentName"],
|
|
||||||
NSPersistentStoreUbiquitousContentURLKey : newCloudContentURL,
|
|
||||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
|
||||||
NSInferMappingModelAutomaticallyOption : @YES};
|
|
||||||
|
|
||||||
// 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:[storeManager URLForCloudStoreDirectory].path
|
|
||||||
withIntermediateDirectories:YES attributes:nil error:&error]) {
|
|
||||||
err(@"While creating directory for new cloud store: %@", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
|
|
||||||
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
|
||||||
|
|
||||||
// Open the old cloud store.
|
|
||||||
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldCloudStoreURL
|
|
||||||
options:oldCloudStoreOptions error:&error];
|
|
||||||
if (!oldStore) {
|
|
||||||
err(@"While opening old store for migration %@: %@", oldCloudStoreURL.path, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbg(@"=== Old store ===");
|
|
||||||
for (NSEntityDescription *entity in model.entities) {
|
|
||||||
NSFetchRequest *fetch = [NSFetchRequest new];
|
|
||||||
fetch.entity = entity;
|
|
||||||
NSManagedObjectContext *moc = [NSManagedObjectContext new];
|
|
||||||
moc.persistentStoreCoordinator = psc;
|
|
||||||
dbg(@"%@: %d", entity.name, [[moc executeFetchRequest:fetch error:&error] count]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate to the new cloud store.
|
|
||||||
if (![psc migratePersistentStore:oldStore toURL:newCloudStoreURL options:newCloudStoreOptions withType:NSSQLiteStoreType
|
|
||||||
error:&error]) {
|
|
||||||
err(@"While migrating cloud store from %@ -> %@: %@", oldCloudStoreURL.path, newCloudStoreURL.path, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbg(@"=== New store ===");
|
|
||||||
for (NSEntityDescription *entity in model.entities) {
|
|
||||||
NSFetchRequest *fetch = [NSFetchRequest new];
|
|
||||||
fetch.entity = entity;
|
|
||||||
NSManagedObjectContext *moc = [NSManagedObjectContext new];
|
|
||||||
moc.persistentStoreCoordinator = psc;
|
|
||||||
dbg(@"%@: %d", entity.name, [[moc executeFetchRequest:fetch error:&error] count]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean-up.
|
|
||||||
if (![psc removePersistentStore:[psc.persistentStores lastObject] error:&error])
|
|
||||||
err(@"While removing the migrated store from the store context: %@", error);
|
|
||||||
if (![[NSFileManager defaultManager] removeItemAtURL:oldCloudStoreURL error:&error])
|
|
||||||
err(@"While deleting the old cloud store: %@", error);
|
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:CloudEnabledKey];
|
|
||||||
inf(@"Successfully migrated old to new cloud store.");
|
|
||||||
} else {
|
|
||||||
NSURL *applicationFilesDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
|
|
||||||
inDomains:NSUserDomainMask] lastObject];
|
|
||||||
NSURL *oldLocalStoreURL = [[applicationFilesDirectory URLByAppendingPathComponent:@"MasterPassword" isDirectory:NO]
|
|
||||||
URLByAppendingPathExtension:@"sqlite"];
|
|
||||||
NSURL *newLocalStoreURL = [storeManager URLForLocalStore];
|
|
||||||
dbg(@"Migrating local:\n%@ ->\n%@", oldLocalStoreURL, newLocalStoreURL);
|
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:oldLocalStoreURL.path isDirectory:NO] &&
|
|
||||||
![[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NO]) {
|
|
||||||
NSError *error = nil;
|
|
||||||
NSDictionary *oldLocalStoreOptions = @{
|
|
||||||
NSReadOnlyPersistentStoreOption : @YES,
|
|
||||||
NSInferMappingModelAutomaticallyOption : @YES
|
|
||||||
};
|
|
||||||
NSDictionary *newLocalStoreOptions = @{
|
|
||||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
|
||||||
NSInferMappingModelAutomaticallyOption : @YES};
|
|
||||||
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
|
|
||||||
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
|
||||||
|
|
||||||
// Create the directory to hold the new local store.
|
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:[storeManager URLForLocalStoreDirectory].path
|
|
||||||
withIntermediateDirectories:YES attributes:nil error:&error]) {
|
|
||||||
err(@"While creating directory for new local store: %@", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the old local store.
|
|
||||||
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
|
|
||||||
URL:oldLocalStoreURL options:oldLocalStoreOptions error:&error];
|
|
||||||
if (!oldStore) {
|
|
||||||
err(@"While opening old store for migration %@: %@", oldLocalStoreURL.path, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate to the new local store.
|
|
||||||
if (![psc migratePersistentStore:oldStore toURL:newLocalStoreURL options:newLocalStoreOptions withType:NSSQLiteStoreType error:&error]) {
|
|
||||||
err(@"While migrating local store from %@ -> %@: %@", oldLocalStoreURL, newLocalStoreURL, error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean-up.
|
|
||||||
if (![psc removePersistentStore:[psc.persistentStores lastObject] error:&error])
|
|
||||||
err(@"While removing the migrated store from the store context: %@", error);
|
|
||||||
|
|
||||||
if (![[NSFileManager defaultManager] removeItemAtURL:oldLocalStoreURL error:&error])
|
|
||||||
err(@"While deleting the old local store: %@", error);
|
|
||||||
inf(@"Successfully migrated old to new local store.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"LocalUUIDKey"];
|
|
||||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"iCloudEnabledKey"];
|
|
||||||
dbg(@"Removed old cloud keys.");
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UbiquityStoreManager *)storeManager {
|
- (UbiquityStoreManager *)storeManager {
|
||||||
|
|
||||||
static UbiquityStoreManager *storeManager = nil;
|
static UbiquityStoreManager *storeManager = nil;
|
||||||
@ -240,13 +87,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
|
|
||||||
storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil
|
storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil
|
||||||
containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
|
containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
|
||||||
#if TARGET_OS_IPHONE
|
additionalStoreOptions:@{STORE_OPTIONS}
|
||||||
additionalStoreOptions:@{
|
|
||||||
NSPersistentStoreFileProtectionKey : NSFileProtectionComplete
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
additionalStoreOptions:nil
|
|
||||||
#endif
|
|
||||||
delegate:self];
|
delegate:self];
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
@ -271,13 +112,187 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
return storeManager;
|
return storeManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)migrateStoreForManager:(UbiquityStoreManager *)manager isCloud:(BOOL)isCloudStore {
|
||||||
|
|
||||||
|
[self migrateLocalStoreForManager:manager];
|
||||||
|
|
||||||
|
if (isCloudStore)
|
||||||
|
[self migrateCloudStoreForManager:manager];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)migrateLocalStoreForManager:(UbiquityStoreManager *)manager {
|
||||||
|
|
||||||
|
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.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inf(@"Migrating local store...");
|
||||||
|
NSError *error = nil;
|
||||||
|
NSDictionary *oldLocalStoreOptions = @{
|
||||||
|
STORE_OPTIONS
|
||||||
|
NSReadOnlyPersistentStoreOption : @YES,
|
||||||
|
NSInferMappingModelAutomaticallyOption : @YES
|
||||||
|
};
|
||||||
|
NSDictionary *newLocalStoreOptions = @{
|
||||||
|
STORE_OPTIONS
|
||||||
|
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
||||||
|
NSInferMappingModelAutomaticallyOption : @YES
|
||||||
|
};
|
||||||
|
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
|
||||||
|
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the old local store.
|
||||||
|
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldLocalStoreURL
|
||||||
|
options:oldLocalStoreOptions error:&error];
|
||||||
|
if (!oldStore) {
|
||||||
|
err(@"While opening old store for migration %@: %@", oldLocalStoreURL.path, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate to the new local store.
|
||||||
|
if (![psc migratePersistentStore:oldStore toURL:newLocalStoreURL options:newLocalStoreOptions withType:NSSQLiteStoreType
|
||||||
|
error:&error]) {
|
||||||
|
err(@"While migrating local store from %@ -> %@: %@", oldLocalStoreURL, newLocalStoreURL, error);
|
||||||
|
manager.localStoreURL = oldLocalStoreURL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean-up.
|
||||||
|
for (NSPersistentStore *store in psc.persistentStores)
|
||||||
|
if (![psc removePersistentStore:store error:&error]) {
|
||||||
|
err(@"While removing the migrated store from the store context: %@", error);
|
||||||
|
}
|
||||||
|
if (![[NSFileManager defaultManager] removeItemAtURL:oldLocalStoreURL error:&error]) {
|
||||||
|
err(@"While deleting the old local store: %@", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
inf(@"Successfully migrated old to new local store.");
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)migrateCloudStoreForManager:(UbiquityStoreManager *)manager {
|
||||||
|
|
||||||
|
// 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.");
|
||||||
|
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"]);
|
||||||
|
|
||||||
|
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,
|
||||||
|
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"],
|
||||||
|
NSPersistentStoreUbiquitousContentURLKey : newCloudContentURL,
|
||||||
|
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
||||||
|
NSInferMappingModelAutomaticallyOption : @YES
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
|
||||||
|
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
||||||
|
|
||||||
|
// Open the old cloud store.
|
||||||
|
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldCloudStoreURL
|
||||||
|
options:oldCloudStoreOptions error:&error];
|
||||||
|
if (!oldStore) {
|
||||||
|
err(@"While opening old store for migration %@: %@", oldCloudStoreURL.path, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate to the new cloud store.
|
||||||
|
if (![psc migratePersistentStore:oldStore toURL:newCloudStoreURL options:newCloudStoreOptions withType:NSSQLiteStoreType
|
||||||
|
error:&error]) {
|
||||||
|
err(@"While migrating cloud store from %@ -> %@: %@", oldCloudStoreURL.path, newCloudStoreURL.path, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean-up.
|
||||||
|
for (NSPersistentStore *store in psc.persistentStores)
|
||||||
|
if (![psc removePersistentStore:store error:&error]) {
|
||||||
|
err(@"While removing the migrated store from the store context: %@", error);
|
||||||
|
}
|
||||||
|
if (![[NSFileManager defaultManager] removeItemAtURL:oldCloudStoreURL error:&error]) {
|
||||||
|
err(@"While deleting the old cloud store: %@", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"LocalUUIDKey"];
|
||||||
|
inf(@"Successfully migrated old to new cloud store.");
|
||||||
|
}
|
||||||
|
|
||||||
- (void)saveContexts {
|
- (void)saveContexts {
|
||||||
|
|
||||||
NSManagedObjectContext *mainManagedObjectContext = self.mainManagedObjectContext;
|
NSManagedObjectContext *mainManagedObjectContext = self.mainManagedObjectContext;
|
||||||
[mainManagedObjectContext performBlockAndWait:^{
|
[mainManagedObjectContext performBlockAndWait:^{
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
if (![mainManagedObjectContext save:&error])
|
if (![mainManagedObjectContext save:&error])
|
||||||
err(@"While saving main context: %@", error);
|
err(@"While saving main context: %@", error);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
NSManagedObjectContext *privateManagedObjectContext = [self privateManagedObjectContextIfReady];
|
NSManagedObjectContext *privateManagedObjectContext = [self privateManagedObjectContextIfReady];
|
||||||
@ -302,16 +317,16 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
|
|
||||||
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore {
|
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore {
|
||||||
|
|
||||||
[self migrateStoreForManager:manager];
|
// FIXME
|
||||||
|
self.privateManagedObjectContext = nil;
|
||||||
|
self.mainManagedObjectContext = nil;
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
if (![self.storeLoading isVisible])
|
if (![self.storeLoading isVisible])
|
||||||
self.storeLoading = [PearlOverlay showOverlayWithTitle:@"Loading..."];
|
self.storeLoading = [PearlOverlay showOverlayWithTitle:@"Opening Your Data"];
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME
|
[self migrateStoreForManager:manager isCloud:isCloudStore];
|
||||||
//self.privateManagedObjectContext = nil;
|
|
||||||
//self.mainManagedObjectContext = nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didLoadStoreForCoordinator:(NSPersistentStoreCoordinator *)coordinator
|
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didLoadStoreForCoordinator:(NSPersistentStoreCoordinator *)coordinator
|
||||||
@ -324,7 +339,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
#endif
|
#endif
|
||||||
#ifdef LOCALYTICS
|
#ifdef LOCALYTICS
|
||||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloud attributes:@{
|
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloud attributes:@{
|
||||||
@"enabled": isCloudStore? @"YES": @"NO"
|
@"enabled": @(isCloudStore)
|
||||||
}];
|
}];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -408,16 +423,18 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
static NSRegularExpression *headerPattern, *sitePattern;
|
static NSRegularExpression *headerPattern, *sitePattern;
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
if (!headerPattern) {
|
if (!headerPattern) {
|
||||||
headerPattern = [[NSRegularExpression alloc] initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
headerPattern = [[NSRegularExpression alloc]
|
||||||
options:0 error:&error];
|
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
||||||
|
options:(NSRegularExpressionOptions)0 error:&error];
|
||||||
if (error) {
|
if (error) {
|
||||||
err(@"Error loading the header pattern: %@", error);
|
err(@"Error loading the header pattern: %@", error);
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!sitePattern) {
|
if (!sitePattern) {
|
||||||
sitePattern = [[NSRegularExpression alloc] initWithPattern:@"^([^[:space:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)(:[[:digit:]]+)?[[:space:]]+([^\t]+)\t(.*)"
|
sitePattern = [[NSRegularExpression alloc]
|
||||||
options:0 error:&error];
|
initWithPattern:@"^([^[:space:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)(:[[:digit:]]+)?[[:space:]]+([^\t]+)\t(.*)"
|
||||||
|
options:(NSRegularExpressionOptions)0 error:&error];
|
||||||
if (error) {
|
if (error) {
|
||||||
err(@"Error loading the site pattern: %@", error);
|
err(@"Error loading the site pattern: %@", error);
|
||||||
return MPImportResultInternalError;
|
return MPImportResultInternalError;
|
||||||
@ -457,11 +474,12 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
if ([headerPattern numberOfMatchesInString:importedSiteLine options:0 range:NSMakeRange(0, [importedSiteLine length])] != 1) {
|
if ([headerPattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||||
|
range:NSMakeRange( 0, [importedSiteLine length] )] != 1) {
|
||||||
err(@"Invalid header format in line: %@", importedSiteLine);
|
err(@"Invalid header format in line: %@", importedSiteLine);
|
||||||
return MPImportResultMalformedInput;
|
return MPImportResultMalformedInput;
|
||||||
}
|
}
|
||||||
NSTextCheckingResult *headerElements = [[headerPattern matchesInString:importedSiteLine options:0
|
NSTextCheckingResult *headerElements = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||||
range:NSMakeRange(0, [importedSiteLine length])] lastObject];
|
range:NSMakeRange(0, [importedSiteLine length])] lastObject];
|
||||||
NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]];
|
NSString *headerName = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:1]];
|
||||||
NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]];
|
NSString *headerValue = [importedSiteLine substringWithRange:[headerElements rangeAtIndex:2]];
|
||||||
@ -504,11 +522,12 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Site
|
// Site
|
||||||
if ([sitePattern numberOfMatchesInString:importedSiteLine options:0 range:NSMakeRange(0, [importedSiteLine length])] != 1) {
|
if ([sitePattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||||
|
range:NSMakeRange( 0, [importedSiteLine length] )] != 1) {
|
||||||
err(@"Invalid site format in line: %@", importedSiteLine);
|
err(@"Invalid site format in line: %@", importedSiteLine);
|
||||||
return MPImportResultMalformedInput;
|
return MPImportResultMalformedInput;
|
||||||
}
|
}
|
||||||
NSTextCheckingResult *siteElements = [[sitePattern matchesInString:importedSiteLine options:0
|
NSTextCheckingResult *siteElements = [[sitePattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||||
range:NSMakeRange(0, [importedSiteLine length])] lastObject];
|
range:NSMakeRange(0, [importedSiteLine length])] lastObject];
|
||||||
NSString *lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
NSString *lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
||||||
NSString *uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
NSString *uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>_XCCurrentVersionName</key>
|
<key>_XCCurrentVersionName</key>
|
||||||
<string>MasterPassword 4.xcdatamodel</string>
|
<string>MasterPassword 1.xcdatamodel</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
@property (weak, nonatomic) IBOutlet UILabel *passwordTipLabel;
|
@property (weak, nonatomic) IBOutlet UILabel *passwordTipLabel;
|
||||||
@property (weak, nonatomic) IBOutlet UIView *wordWall;
|
@property (weak, nonatomic) IBOutlet UIView *wordWall;
|
||||||
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *targetedUserActionGesture;
|
@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *targetedUserActionGesture;
|
||||||
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *loadingUsersIndicator;
|
|
||||||
@property (weak, nonatomic) IBOutlet UIView *uiContainer;
|
@property (weak, nonatomic) IBOutlet UIView *uiContainer;
|
||||||
@property (weak, nonatomic) IBOutlet UIWebView *newsView;
|
@property (weak, nonatomic) IBOutlet UIWebView *newsView;
|
||||||
|
|
||||||
|
@ -203,14 +203,10 @@
|
|||||||
- (void)updateUsers {
|
- (void)updateUsers {
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [MPAppDelegate managedObjectContextForThreadIfReady];
|
NSManagedObjectContext *moc = [MPAppDelegate managedObjectContextForThreadIfReady];
|
||||||
if (!moc) {
|
if (!moc)
|
||||||
self.tip.text = @"Loading...";
|
|
||||||
[self.loadingUsersIndicator startAnimating];
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
self.tip.text = @"Tap and hold to delete or reset.";
|
self.tip.text = @"Tap and hold to delete or reset.";
|
||||||
[self.loadingUsersIndicator stopAnimating];
|
|
||||||
|
|
||||||
__block NSArray *users = nil;
|
__block NSArray *users = nil;
|
||||||
[moc performBlockAndWait:^{
|
[moc performBlockAndWait:^{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="2.0" toolsVersion="3084" systemVersion="12C60" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="KZF-fe-y9n">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="2.0" toolsVersion="3084" systemVersion="12D78" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="KZF-fe-y9n">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="2083"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="2083"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@ -1455,7 +1455,7 @@ Pink fluffy door frame.</string>
|
|||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
</view>
|
</view>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="Loading..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="DBJ-Qi-ZcF">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="" 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="460" width="280" height="20"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="12"/>
|
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="12"/>
|
||||||
@ -1656,10 +1656,6 @@ You could use the word wall for inspiration in finding a memorable master passw
|
|||||||
<outlet property="delegate" destination="Nbn-Rv-sP1" id="ZNn-UN-gcu"/>
|
<outlet property="delegate" destination="Nbn-Rv-sP1" id="ZNn-UN-gcu"/>
|
||||||
</connections>
|
</connections>
|
||||||
</webView>
|
</webView>
|
||||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" hidesWhenStopped="YES" animating="YES" style="whiteLarge" id="XuP-pi-ygN">
|
|
||||||
<rect key="frame" x="141" y="415" width="37" height="37"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
|
||||||
</activityIndicatorView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
</view>
|
</view>
|
||||||
@ -1671,7 +1667,6 @@ You could use the word wall for inspiration in finding a memorable master passw
|
|||||||
<outlet property="avatarTemplate" destination="Ten-ig-gog" id="0ZZ-z5-d5m"/>
|
<outlet property="avatarTemplate" destination="Ten-ig-gog" id="0ZZ-z5-d5m"/>
|
||||||
<outlet property="avatarsView" destination="Blg-F1-9NA" id="2NL-jU-IMI"/>
|
<outlet property="avatarsView" destination="Blg-F1-9NA" id="2NL-jU-IMI"/>
|
||||||
<outlet property="createPasswordTipView" destination="xWL-xQ-KjX" id="Pa1-Bk-pW2"/>
|
<outlet property="createPasswordTipView" destination="xWL-xQ-KjX" id="Pa1-Bk-pW2"/>
|
||||||
<outlet property="loadingUsersIndicator" destination="XuP-pi-ygN" id="idh-aH-sDL"/>
|
|
||||||
<outlet property="nameLabel" destination="0NM-NI-7UR" id="GBg-Ry-sqj"/>
|
<outlet property="nameLabel" destination="0NM-NI-7UR" id="GBg-Ry-sqj"/>
|
||||||
<outlet property="newsView" destination="rGU-aZ-XVm" id="p1r-Wf-YIY"/>
|
<outlet property="newsView" destination="rGU-aZ-XVm" id="p1r-Wf-YIY"/>
|
||||||
<outlet property="oldNameLabel" destination="8s0-nT-Aoq" id="plu-1H-MVc"/>
|
<outlet property="oldNameLabel" destination="8s0-nT-Aoq" id="plu-1H-MVc"/>
|
||||||
@ -2235,12 +2230,156 @@ You could use the word wall for inspiration in finding a memorable master passw
|
|||||||
<image name="ui_spinner.png" width="75" height="75"/>
|
<image name="ui_spinner.png" width="75" height="75"/>
|
||||||
<image name="ui_textfield.png" width="158" height="34"/>
|
<image name="ui_textfield.png" width="158" height="34"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
<classes>
|
||||||
|
<class className="MPAppViewController" superclassName="UIViewController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPAppViewController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="action" name="deblock:" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="action" name="gorillas:" candidateClass="UIButton"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
<class className="MPAppsViewController" superclassName="UIViewController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPAppsViewController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="action" name="exit"/>
|
||||||
|
<relationship kind="outlet" name="pagePositionView" candidateClass="UIImageView"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
<class className="MPElementListAllViewController" superclassName="MPElementListController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPElementListAllViewController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="action" name="add:"/>
|
||||||
|
<relationship kind="action" name="close:"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
<class className="MPElementListController" superclassName="UITableViewController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPElementListController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="outlet" name="delegate"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
<class className="MPElementListSearchController" superclassName="MPElementListController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPElementListSearchController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="outlet" name="searchDisplayController" candidateClass="UISearchDisplayController"/>
|
||||||
|
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
<class className="MPGuideViewController" superclassName="UIViewController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPGuideViewController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="action" name="close"/>
|
||||||
|
<relationship kind="outlet" name="pageControl" candidateClass="UIPageControl"/>
|
||||||
|
<relationship kind="outlet" name="scrollView" candidateClass="UIScrollView"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
<class className="MPMainViewController" superclassName="UIViewController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPMainViewController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="action" name="action:" candidateClass="UIBarButtonItem"/>
|
||||||
|
<relationship kind="action" name="closeAlert"/>
|
||||||
|
<relationship kind="action" name="closeOutdatedAlert"/>
|
||||||
|
<relationship kind="action" name="copyContent"/>
|
||||||
|
<relationship kind="action" name="editLoginName:" candidateClass="UILongPressGestureRecognizer"/>
|
||||||
|
<relationship kind="action" name="editPassword"/>
|
||||||
|
<relationship kind="action" name="incrementPasswordCounter"/>
|
||||||
|
<relationship kind="action" name="infoOutdatedAlert"/>
|
||||||
|
<relationship kind="action" name="panHelpDown:" candidateClass="UIPanGestureRecognizer"/>
|
||||||
|
<relationship kind="action" name="panHelpUp:" candidateClass="UIPanGestureRecognizer"/>
|
||||||
|
<relationship kind="action" name="resetPasswordCounter:" candidateClass="UILongPressGestureRecognizer"/>
|
||||||
|
<relationship kind="action" name="searchOutdatedElements"/>
|
||||||
|
<relationship kind="action" name="toggleUser"/>
|
||||||
|
<relationship kind="action" name="upgradePassword"/>
|
||||||
|
<relationship kind="outlet" name="actionsTipContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="alertBody" candidateClass="UITextView"/>
|
||||||
|
<relationship kind="outlet" name="alertContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="alertTitle" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="contentContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="contentField" candidateClass="UITextField"/>
|
||||||
|
<relationship kind="outlet" name="contentTipBody" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="contentTipContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="displayContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="helpContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="helpView" candidateClass="UIWebView"/>
|
||||||
|
<relationship kind="outlet" name="loginNameContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="loginNameField" candidateClass="UITextField"/>
|
||||||
|
<relationship kind="outlet" name="loginNameTipBody" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="loginNameTipContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="outdatedAlertBack" candidateClass="UIImageView"/>
|
||||||
|
<relationship kind="outlet" name="outdatedAlertCloseButton" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="outdatedAlertContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="passwordCounter" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="passwordEdit" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="passwordIncrementer" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="passwordUpgrade" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="passwordUser" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="pullDownGesture" candidateClass="UIPanGestureRecognizer"/>
|
||||||
|
<relationship kind="outlet" name="pullDownView" candidateClass="UIImageView"/>
|
||||||
|
<relationship kind="outlet" name="pullUpGesture" candidateClass="UIPanGestureRecognizer"/>
|
||||||
|
<relationship kind="outlet" name="pullUpView" candidateClass="UIImageView"/>
|
||||||
|
<relationship kind="outlet" name="searchDelegate" candidateClass="MPElementListSearchController"/>
|
||||||
|
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="siteName" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="toolTipBody" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="toolTipContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="toolTipEditIcon" candidateClass="UIImageView"/>
|
||||||
|
<relationship kind="outlet" name="typeButton" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="typeTipContainer" candidateClass="UIView"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
<class className="MPPreferencesViewController" superclassName="UITableViewController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPPreferencesViewController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="action" name="didToggleSwitch:" candidateClass="UISwitch"/>
|
||||||
|
<relationship kind="action" name="settings:"/>
|
||||||
|
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
|
||||||
|
<relationship kind="outlet" name="changeMPCell" candidateClass="UITableViewCell"/>
|
||||||
|
<relationship kind="outlet" name="defaultTypeLabel" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="exportCell" candidateClass="UITableViewCell"/>
|
||||||
|
<relationship kind="outlet" name="savePasswordSwitch" candidateClass="UISwitch"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
<class className="MPTypeViewController" superclassName="UITableViewController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPTypeViewController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="outlet" name="recommendedTipContainer" candidateClass="UIView"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
<class className="MPUnlockViewController" superclassName="UIViewController">
|
||||||
|
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPUnlockViewController.h"/>
|
||||||
|
<relationships>
|
||||||
|
<relationship kind="action" name="add:" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="action" name="facebook:" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="action" name="google:" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="action" name="mail:" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="action" name="targetedUserAction:" candidateClass="UILongPressGestureRecognizer"/>
|
||||||
|
<relationship kind="action" name="twitter:" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
|
||||||
|
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
|
||||||
|
<relationship kind="outlet" name="createPasswordTipView" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="nameLabel" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="newsView" candidateClass="UIWebView"/>
|
||||||
|
<relationship kind="outlet" name="oldNameLabel" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="passwordField" candidateClass="UITextField"/>
|
||||||
|
<relationship kind="outlet" name="passwordFieldLabel" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="passwordTipLabel" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="passwordTipView" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="passwordView" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="spinner" candidateClass="UIImageView"/>
|
||||||
|
<relationship kind="outlet" name="targetedUserActionGesture" candidateClass="UILongPressGestureRecognizer"/>
|
||||||
|
<relationship kind="outlet" name="tip" candidateClass="UILabel"/>
|
||||||
|
<relationship kind="outlet" name="uiContainer" candidateClass="UIView"/>
|
||||||
|
<relationship kind="outlet" name="wordWall" candidateClass="UIView"/>
|
||||||
|
</relationships>
|
||||||
|
</class>
|
||||||
|
</classes>
|
||||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||||
<nil key="statusBar"/>
|
<nil key="statusBar"/>
|
||||||
<simulatedOrientationMetrics key="orientation"/>
|
<simulatedOrientationMetrics key="orientation"/>
|
||||||
<simulatedScreenMetrics key="destination"/>
|
<simulatedScreenMetrics key="destination"/>
|
||||||
</simulatedMetricsContainer>
|
</simulatedMetricsContainer>
|
||||||
<inferredMetricsTieBreakers>
|
<inferredMetricsTieBreakers>
|
||||||
<segue reference="9Bs-cD-ddF"/>
|
<segue reference="KIl-ZW-M7G"/>
|
||||||
</inferredMetricsTieBreakers>
|
</inferredMetricsTieBreakers>
|
||||||
</document>
|
</document>
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction>
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
<BuildActionEntries>
|
<BuildActionEntries>
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForRunning = "YES">
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||||
@ -15,9 +21,32 @@
|
|||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
</BuildAction>
|
</BuildAction>
|
||||||
<LaunchAction
|
<TestAction
|
||||||
useCustomWorkingDirectory = "NO"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
buildConfiguration = "Debug">
|
buildConfiguration = "Debug">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||||
|
BuildableName = "MasterPassword.app"
|
||||||
|
BlueprintName = "MasterPassword"
|
||||||
|
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
<BuildableProductRunnable>
|
<BuildableProductRunnable>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
@ -27,5 +56,30 @@
|
|||||||
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
|
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||||
|
BuildableName = "MasterPassword.app"
|
||||||
|
BlueprintName = "MasterPassword"
|
||||||
|
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
</Scheme>
|
</Scheme>
|
||||||
|
Loading…
Reference in New Issue
Block a user