2
0

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:
Maarten Billemont 2013-01-25 01:05:31 -05:00
parent 4c19a29897
commit b07298e203
5 changed files with 111 additions and 82 deletions

@ -1 +1 @@
Subproject commit 20e2a1dec08fef1e99433f9a8b3a2310bb00717d Subproject commit 8faba0d3c35d471005c45280dc2670bc811e08c6

View File

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

View File

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

View File

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

View File

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