Last active
June 20, 2016 20:50
-
-
Save kean/934c69d5b567e3d4c9a6f512542a59ee to your computer and use it in GitHub Desktop.
DFCoreDataMigration.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The MIT License (MIT) | |
// | |
// Copyright (c) 2016 Alexander Grebenyuk (github.com/kean). | |
#import "DFCoreDataMigration.h" | |
NSString *const DFMigrationErrorDomain = @"DFMigrationErrorDomain"; | |
@implementation DFMigrationManager | |
- (instancetype)initWithStoreURL:(NSURL *)storeURL storeType:(NSString *)storeType orderedModels:(NSArray<NSManagedObjectModel *> *)models { | |
NSParameterAssert(storeURL); | |
NSParameterAssert(storeType); | |
NSParameterAssert(models.count > 0); | |
if (self = [super init]) { | |
_storeURL = [storeURL copy]; | |
_storeType = [storeType copy]; | |
_moms = [models copy]; | |
_bundles = [NSBundle allBundles]; | |
} | |
return self; | |
} | |
- (BOOL)migrate:(NSError *__autoreleasing _Nullable *)error { | |
NSInteger modelIndex = [self _indexOfCompatibleMom:error]; | |
if (modelIndex == NSNotFound) { | |
if (error != NULL && *error == nil) { | |
*error = [NSError errorWithDomain:DFMigrationErrorDomain code:DFMigrationErrorSourceModelNotFound userInfo:@{ NSLocalizedDescriptionKey: @"Failed to find a managed object model compatible with the persistent store." }]; | |
} | |
return NO; | |
} | |
if (modelIndex == (self.moms.count - 1)) { | |
return YES; // Migrated to the final model | |
} | |
@autoreleasepool { | |
NSManagedObjectModel *smom = self.moms[modelIndex]; | |
NSManagedObjectModel *dmom = self.moms[modelIndex+1]; | |
if (![self _migrateFromMom:smom toMom:dmom error:error]) { | |
return NO; | |
} | |
} | |
return [self migrate:error]; | |
} | |
- (NSInteger)_compatibleMomIndex:(NSError **)error { | |
NSDictionary *meta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:self.storeType URL:self.storeURL options:self.storeOptions error:error]; | |
return meta ? [self.moms indexOfObjectPassingTest:^BOOL(NSManagedObjectModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { | |
return [obj isConfiguration:self.storeConfiguraton compatibleWithStoreMetadata:meta]; | |
}] : NSNotFound; | |
} | |
- (BOOL)_migrateFromMom:(NSManagedObjectModel *)smom toMom:(NSManagedObjectModel *)dmom error:(NSError *__autoreleasing _Nullable *)error { | |
// Create temp directory for destination store | |
NSURL *tmpDir = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:[NSUUID UUID].UUIDString]; | |
if (![[NSFileManager defaultManager] createDirectoryAtURL:tmpDir withIntermediateDirectories:YES attributes:nil error:error]) { | |
return NO; | |
} | |
BOOL didMigrate = [self _migrateFromMom:smom toMom:dmom tmpDir:tmpDir error:error]; | |
// Cleanup | |
[[NSFileManager defaultManager] removeItemAtURL:tmpDir error:nil]; | |
return didMigrate; | |
} | |
- (BOOL)_migrateFromMom:(NSManagedObjectModel *)smom toMom:(NSManagedObjectModel *)dmom tmpDir:(NSURL *)tmpDir error:(NSError *__autoreleasing _Nullable *)error { | |
NSArray<NSMappingModel *> *mappings = [self _mappingsFromMom:smom toMom:dmom error:error]; | |
if (!mappings.count) { | |
return NO; | |
} | |
// Migrate store to temp destination | |
NSURL *destinationURL = [tmpDir URLByAppendingPathComponent:self.storeURL.lastPathComponent]; | |
NSMigrationManager *manager = [[NSMigrationManager alloc] initWithSourceModel:smom destinationModel:dmom]; | |
for (NSMappingModel *mapping in mappings) { | |
@autoreleasepool { | |
if (![manager migrateStoreFromURL:self.storeURL | |
type:self.storeType | |
options:self.storeOptions | |
withMappingModel:mapping | |
toDestinationURL:destinationURL | |
destinationType:self.storeType | |
destinationOptions:self.storeOptions | |
error:error]) { | |
return NO; | |
} | |
} | |
} | |
// Overwrites source store with the migrated store. | |
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:dmom]; | |
if (![psc replacePersistentStoreAtURL:self.storeURL | |
destinationOptions:self.storeOptions | |
withPersistentStoreFromURL:destinationURL | |
sourceOptions:self.storeOptions | |
storeType:self.storeType | |
error:error]) { | |
return NO; | |
} | |
return YES; | |
} | |
- (NSArray<NSMappingModel *> *)_mappingsFromMom:(NSManagedObjectModel *)smom toMom:(NSManagedObjectModel *)dmom error:(NSError *__autoreleasing _Nullable *)error { | |
// Find custom mapping | |
NSMappingModel *mapping = [NSMappingModel mappingModelFromBundles:self.bundles forSourceModel:smom destinationModel:dmom]; | |
if (!mapping) { | |
// Find inferred mapping (lightweight migration) | |
mapping = [NSMappingModel inferredMappingModelForSourceModel:smom destinationModel:dmom error:error]; | |
} | |
if (!mapping) { | |
if (error != NULL && *error == nil) { | |
*error = [NSError errorWithDomain:DFMigrationErrorDomain code:DFMigrationErrorMappingModelNotFound userInfo:@{ NSLocalizedDescriptionKey: @"Failed to find mapping model" }]; | |
} | |
} | |
return mapping ? @[mapping] : nil; | |
} | |
@end | |
@implementation DFMigrationManager (Convenience) | |
+ (BOOL)migrateStoreAtURL:(NSURL *)storeURL storeType:(NSString *)storeType orderedModels:(NSArray<NSManagedObjectModel *> *)models error:(NSError *__autoreleasing _Nullable *)error { | |
return [[[DFMigrationManager alloc] initWithStoreURL:storeURL storeType:storeType orderedModels:models] migrate:error]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment