Fixed migration of old store to new store + don't hold references to CoreData objects.
[FIXED] Working migration of old store to new store. [FIXED] We shouldn't be holding references to CoreData objects anywhere. In that light, the user NSMenuItems have been fixed.
This commit is contained in:
parent
4c19a29897
commit
b07298e203
2
External/iCloudStoreManager
vendored
2
External/iCloudStoreManager
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 20e2a1dec08fef1e99433f9a8b3a2310bb00717d
|
Subproject commit 8faba0d3c35d471005c45280dc2670bc811e08c6
|
@ -16,6 +16,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
@property (strong, nonatomic) MPUserEntity *activeUser;
|
@property (strong, nonatomic) MPUserEntity *activeUser;
|
||||||
|
@property (strong, nonatomic) NSManagedObjectID *activeUserObjectID;
|
||||||
@property (strong, nonatomic) MPKey *key;
|
@property (strong, nonatomic) MPKey *key;
|
||||||
|
|
||||||
+ (MPAppDelegate_Shared *)get;
|
+ (MPAppDelegate_Shared *)get;
|
||||||
|
@ -9,12 +9,6 @@
|
|||||||
#import "MPAppDelegate_Shared.h"
|
#import "MPAppDelegate_Shared.h"
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
|
|
||||||
@interface MPAppDelegate_Shared ()
|
|
||||||
|
|
||||||
@property (strong, nonatomic) NSManagedObjectID *activeUserID;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation MPAppDelegate_Shared
|
@implementation MPAppDelegate_Shared
|
||||||
|
|
||||||
+ (MPAppDelegate_Shared *)get {
|
+ (MPAppDelegate_Shared *)get {
|
||||||
@ -30,15 +24,15 @@
|
|||||||
|
|
||||||
- (MPUserEntity *)activeUser {
|
- (MPUserEntity *)activeUser {
|
||||||
|
|
||||||
if (!self.activeUserID)
|
if (!self.activeUserObjectID)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
return (MPUserEntity *)[self.managedObjectContextIfReady objectWithID:self.activeUserID];
|
return (MPUserEntity *)[self.managedObjectContextIfReady objectWithID:self.activeUserObjectID];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setActiveUser:(MPUserEntity *)activeUser {
|
- (void)setActiveUser:(MPUserEntity *)activeUser {
|
||||||
|
|
||||||
self.activeUserID = activeUser.objectID;
|
self.activeUserObjectID = activeUser.objectID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
#import "MPAppDelegate_Store.h"
|
#import "MPAppDelegate_Store.h"
|
||||||
#import
|
|
||||||
#import
|
|
||||||
|
|
||||||
@implementation MPAppDelegate_Shared (Store)
|
@implementation MPAppDelegate_Shared (Store)
|
||||||
|
|
||||||
@ -42,58 +40,76 @@ static char managedObjectContextKey;
|
|||||||
return managedObjectContext;
|
return managedObjectContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UbiquityStoreManager *)storeManager {
|
- (void)migrateStoreForManager:(UbiquityStoreManager *)storeManager {
|
||||||
|
|
||||||
static UbiquityStoreManager *storeManager = nil;
|
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"iCloudEnabledKey"];
|
||||||
if (storeManager)
|
|
||||||
return storeManager;
|
|
||||||
|
|
||||||
storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil
|
|
||||||
containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
additionalStoreOptions:@{
|
|
||||||
NSPersistentStoreFileProtectionKey : NSFileProtectionComplete
|
|
||||||
}];
|
|
||||||
#else
|
|
||||||
additionalStoreOptions:nil];
|
|
||||||
#endif
|
|
||||||
storeManager.delegate = self;
|
|
||||||
|
|
||||||
// Migrate old store to new store location.
|
|
||||||
NSNumber *cloudEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"iCloudEnabledKey"];
|
NSNumber *cloudEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"iCloudEnabledKey"];
|
||||||
if (cloudEnabled) {
|
if (!cloudEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
if ([cloudEnabled boolValue]) {
|
if ([cloudEnabled boolValue]) {
|
||||||
NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"];
|
|
||||||
NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"];
|
|
||||||
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"];
|
|
||||||
NSURL *newCloudContentURL = [storeManager URLForCloudContent];
|
NSURL *newCloudContentURL = [storeManager URLForCloudContent];
|
||||||
NSURL *newCloudStoreURL = [storeManager URLForCloudStore];
|
NSURL *newCloudStoreURL = [storeManager URLForCloudStore];
|
||||||
|
if ([[NSFileManager defaultManager] fileExistsAtPath:newCloudStoreURL.path isDirectory:NO])
|
||||||
|
// New store already exists, migration has already been done.
|
||||||
|
return;
|
||||||
|
|
||||||
|
NSString *uuid = [[NSUserDefaults standardUserDefaults] stringForKey:@"LocalUUIDKey"];
|
||||||
|
NSURL *cloudContainerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"];
|
||||||
|
//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"];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:oldCloudStoreURL.path isDirectory:NO] &&
|
|
||||||
![[NSFileManager defaultManager] fileExistsAtPath:newCloudStoreURL.path isDirectory:NO]) {
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
NSDictionary *options = @{
|
NSDictionary *oldCloudStoreOptions = @{
|
||||||
NSPersistentStoreUbiquitousContentNameKey : uuid,
|
// This is here in an attempt to have iCloud recreate the old store file from
|
||||||
NSPersistentStoreUbiquitousContentURLKey : oldCloudContentURL,
|
// 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,
|
||||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
||||||
NSInferMappingModelAutomaticallyOption : @YES};
|
NSInferMappingModelAutomaticallyOption : @YES};
|
||||||
|
NSDictionary *newCloudStoreOptions = @{
|
||||||
|
NSPersistentStoreUbiquitousContentNameKey : [storeManager valueForKey:@"contentName"],
|
||||||
|
NSPersistentStoreUbiquitousContentURLKey : newCloudContentURL,
|
||||||
|
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
||||||
|
NSInferMappingModelAutomaticallyOption : @YES};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[storeManager URLForCloudStoreDirectory].path
|
||||||
|
withIntermediateDirectories:YES attributes:nil error:&error])
|
||||||
|
err(@"While creating directory for new cloud store: %@", error);
|
||||||
|
|
||||||
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
|
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
|
||||||
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
|
||||||
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
|
NSPersistentStore *oldStore = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldCloudStoreURL
|
||||||
URL:oldCloudStoreURL options:options error:&error];
|
options:oldCloudStoreOptions error:&error];
|
||||||
if (oldStore)
|
if (!oldStore) {
|
||||||
[psc migratePersistentStore:oldStore toURL:newCloudStoreURL options:options withType:NSSQLiteStoreType error:&error];
|
err(@"While opening old store for migration %@: %@", oldCloudStoreURL.path, error);
|
||||||
if (error)
|
return;
|
||||||
err(@"While migrating cloud store from %@ -> %@: %@", oldCloudStoreURL, newCloudStoreURL, error);
|
|
||||||
else {
|
|
||||||
[psc removePersistentStore:[psc.persistentStores lastObject] error:nil];
|
|
||||||
[[NSFileManager defaultManager] removeItemAtURL:oldCloudStoreURL error:nil];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (![psc migratePersistentStore:oldStore toURL:newCloudStoreURL options:newCloudStoreOptions withType:NSSQLiteStoreType
|
||||||
|
error:&error]) {
|
||||||
|
err(@"While migrating cloud store from %@ -> %@: %@", oldCloudStoreURL.path, newCloudStoreURL.path, error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
} else {
|
} else {
|
||||||
NSURL *applicationFilesDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
|
NSURL *applicationFilesDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
|
||||||
inDomains:NSUserDomainMask] lastObject];
|
inDomains:NSUserDomainMask] lastObject];
|
||||||
@ -120,8 +136,28 @@ static char managedObjectContextKey;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"iCloudEnabledKey"];
|
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"iCloudEnabledKey"];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UbiquityStoreManager *)storeManager {
|
||||||
|
|
||||||
|
static UbiquityStoreManager *storeManager = nil;
|
||||||
|
if (storeManager)
|
||||||
|
return storeManager;
|
||||||
|
|
||||||
|
storeManager = [[UbiquityStoreManager alloc] initStoreNamed:nil withManagedObjectModel:nil localStoreURL:nil
|
||||||
|
containerIdentifier:@"HL3Q45LX9N.com.lyndir.lhunath.MasterPassword.shared"
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
additionalStoreOptions:@{
|
||||||
|
NSPersistentStoreFileProtectionKey : NSFileProtectionComplete
|
||||||
|
}];
|
||||||
|
#else
|
||||||
|
additionalStoreOptions:nil];
|
||||||
|
#endif
|
||||||
|
storeManager.delegate = self;
|
||||||
|
|
||||||
|
// Migrate old store to new store location.
|
||||||
|
[self migrateStoreForManager:storeManager];
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification
|
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification
|
||||||
object:storeManager queue:nil
|
object:storeManager queue:nil
|
||||||
|
@ -107,7 +107,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
for (MPUserEntity *user in users) {
|
for (MPUserEntity *user in users) {
|
||||||
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector(selectUser:) keyEquivalent:@""];
|
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector(selectUser:) keyEquivalent:@""];
|
||||||
[userItem setTarget:self];
|
[userItem setTarget:self];
|
||||||
[userItem setRepresentedObject:user];
|
[userItem setRepresentedObject:user.objectID];
|
||||||
[[self.usersItem submenu] addItem:userItem];
|
[[self.usersItem submenu] addItem:userItem];
|
||||||
|
|
||||||
if ([user.name isEqualToString:[MPMacConfig get].usedUserName])
|
if ([user.name isEqualToString:[MPMacConfig get].usedUserName])
|
||||||
@ -118,9 +118,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
|
|
||||||
- (void)selectUser:(NSMenuItem *)item {
|
- (void)selectUser:(NSMenuItem *)item {
|
||||||
|
|
||||||
NSAssert1([[item representedObject] isKindOfClass:[MPUserEntity class]], @"Not a user: %@", item.representedObject);
|
self.activeUserObjectID = item.representedObject;
|
||||||
|
|
||||||
self.activeUser = item.representedObject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)showMenu {
|
- (void)showMenu {
|
||||||
@ -200,14 +198,14 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
|||||||
|
|
||||||
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
|
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
|
||||||
[[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
[[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
||||||
if ([obj representedObject] && [obj representedObject] == self.activeUser)
|
if ([[obj representedObject] isEqual:self.activeUserObjectID])
|
||||||
[obj setState:NSOnState];
|
[obj setState:NSOnState];
|
||||||
else
|
else
|
||||||
[obj setState:NSOffState];
|
[obj setState:NSOffState];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[MPMacConfig get].usedUserName = self.activeUser.name;
|
[MPMacConfig get].usedUserName = self.activeUser.name;
|
||||||
} forKeyPath:@"activeUser" options:0 context:nil];
|
} forKeyPath:@"activeUserObjectID" options:0 context:nil];
|
||||||
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
|
[[NSNotificationCenter defaultCenter] addObserverForName:UbiquityManagedStoreDidChangeNotification object:nil queue:nil usingBlock:
|
||||||
^(NSNotification *note) {
|
^(NSNotification *note) {
|
||||||
[self updateUsers];
|
[self updateUsers];
|
||||||
|
Loading…
Reference in New Issue
Block a user