Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // The MIT License (MIT)
- //
- // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean).
- #import "DFMigrationManager.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];
- _models = [models copy];
- }
- return self;
- }
- - (BOOL)migrate:(NSError *__autoreleasing _Nullable *)error {
- NSInteger modelIndex = [self _indexOfModelCompatibleWithTheStore: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.models.count - 1)) {
- return YES; // Migrated to the final model
- }
- @autoreleasepool {
- NSManagedObjectModel *sourceModel = self.models[modelIndex];
- NSManagedObjectModel *destinationModel = [self destinationModelForSourceModel:sourceModel sourceModelIndex:modelIndex];
- if (![self migrateFromSourceModel:sourceModel toDestinationModel:destinationModel error:error]) {
- return NO;
- }
- }
- return [self migrate:error];
- }
- - (NSInteger)_indexOfModelCompatibleWithTheStore:(NSError **)error {
- NSDictionary *metadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:self.storeType URL:self.storeURL options:self.storeOptions error:error];
- return metadata ? [self.models indexOfObjectPassingTest:^BOOL(NSManagedObjectModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
- return [obj isConfiguration:self.storeConfiguraton compatibleWithStoreMetadata:metadata];
- }] : NSNotFound;
- }
- - (NSManagedObjectModel *)destinationModelForSourceModel:(NSManagedObjectModel *)sourceModel sourceModelIndex:(NSInteger)sourceModelIndex {
- if (sourceModelIndex == self.models.count + 1) {
- [NSException raise:NSInvalidArgumentException format:@"Attempting to find a destination model for the final model"];
- }
- return self.models[(sourceModelIndex + 1)];
- }
- - (BOOL)migrateFromSourceModel:(NSManagedObjectModel *)sourceModel toDestinationModel:(NSManagedObjectModel *)destinationModel error:(NSError *__autoreleasing _Nullable *)error {
- NSArray<NSMappingModel *> *mappings = [self mappingModelsForSourceModel:sourceModel destinationModel:destinationModel error:error];
- if (!mappings.count) {
- return NO;
- }
- // Create temp directory for destination store
- NSURL *tempDirectory = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:[NSUUID UUID].UUIDString];
- if (![[NSFileManager defaultManager] createDirectoryAtURL:tempDirectory withIntermediateDirectories:YES attributes:nil error:error]) {
- return NO;
- }
- // Migrate store to temp destination
- NSURL *destinationURL = [tempDirectory URLByAppendingPathComponent:self.storeURL.lastPathComponent];
- NSMigrationManager *manager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel destinationModel:destinationModel];
- 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;
- }
- }
- }
- // Remove source store and move destination store to source store location
- if (![self replaceStoreAtURL:self.storeURL withStoreFromURL:destinationURL error:error]) {
- return NO;
- }
- // Cleanup
- [[NSFileManager defaultManager] removeItemAtURL:tempDirectory error:nil];
- return YES;
- }
- - (NSArray<NSMappingModel *> *)mappingModelsForSourceModel:(NSManagedObjectModel *)sourceModel destinationModel:(NSManagedObjectModel *)destinationModel error:(NSError *__autoreleasing _Nullable *)error {
- // Find custom mapping
- NSMappingModel *mapping = [NSMappingModel mappingModelFromBundles:@[[NSBundle mainBundle]] forSourceModel:sourceModel destinationModel:destinationModel];
- if (!mapping) {
- // Find inferred mapping (lightweight migration)
- mapping = [NSMappingModel inferredMappingModelForSourceModel:sourceModel destinationModel:destinationModel error:error];
- }
- if (!mapping) {
- if (error != NULL && *error == nil) {
- *error = [NSError errorWithDomain:DFMigrationErrorDomain code:DFMigrationErrorMappingModelNotFound userInfo:@{ NSLocalizedDescriptionKey: @"Failed to find a mapping model" }];
- }
- }
- return mapping ? @[mapping] : nil;
- }
- // FIXME: Write safer version. Check out `-replacePersistentStoreAtURL:destinationOptions:withPersistentStoreFromURL:sourceOptions:storeType:error:` method.
- - (BOOL)replaceStoreAtURL:(NSURL *)destinationURL withStoreFromURL:(NSURL *)sourceURL error:(NSError *__autoreleasing _Nullable *)error {
- // Remove all destination store's files, including -wal, -shm files, etc.
- for (NSURL *URL in [self _URLsForStoreURL:destinationURL]) {
- if (![[NSFileManager defaultManager] removeItemAtURL:URL error:error]) {
- return NO;
- }
- }
- // Move source store's files to destination store location.
- NSURL *destinationDirectoryURL = destinationURL.URLByDeletingLastPathComponent;
- for (NSURL *fromURL in [self _URLsForStoreURL:sourceURL]) {
- NSURL *toURL = [destinationDirectoryURL URLByAppendingPathComponent:fromURL.lastPathComponent];
- if (![[NSFileManager defaultManager] moveItemAtURL:fromURL toURL:toURL error:error]) {
- return NO;
- }
- }
- return YES;
- }
- - (NSArray<NSURL *> *)_URLsForStoreURL:(NSURL *)storeURL {
- NSURL *storeDirectoryURL = [storeURL URLByDeletingLastPathComponent];
- NSArray<NSURL *> *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:storeDirectoryURL includingPropertiesForKeys:nil options:kNilOptions error:nil];
- return [contents filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSURL * _Nonnull obj, NSDictionary<NSString *,id> * _Nullable bindings) {
- return [obj.lastPathComponent containsString:storeURL.lastPathComponent];
- }]];
- }
- @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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement