Advertisement
Guest User

Untitled

a guest
May 26th, 2016
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.13 KB | None | 0 0
  1. // The MIT License (MIT)
  2. //
  3. // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean).
  4.  
  5. #import "DFMigrationManager.h"
  6.  
  7. NSString *const DFMigrationErrorDomain = @"DFMigrationErrorDomain";
  8.  
  9. @implementation DFMigrationManager
  10.  
  11. - (instancetype)initWithStoreURL:(NSURL *)storeURL storeType:(NSString *)storeType orderedModels:(NSArray<NSManagedObjectModel *> *)models {
  12. NSParameterAssert(storeURL);
  13. NSParameterAssert(storeType);
  14. NSParameterAssert(models.count > 0);
  15. if (self = [super init]) {
  16. _storeURL = [storeURL copy];
  17. _storeType = [storeType copy];
  18. _models = [models copy];
  19. }
  20. return self;
  21. }
  22.  
  23. - (BOOL)migrate:(NSError *__autoreleasing _Nullable *)error {
  24. NSInteger modelIndex = [self _indexOfModelCompatibleWithTheStore:error];
  25. if (modelIndex == NSNotFound) {
  26. if (error != NULL && *error == nil) {
  27. *error = [NSError errorWithDomain:DFMigrationErrorDomain code:DFMigrationErrorSourceModelNotFound userInfo:@{ NSLocalizedDescriptionKey: @"Failed to find a managed object model compatible with the persistent store." }];
  28. }
  29. return NO;
  30. }
  31. if (modelIndex == (self.models.count - 1)) {
  32. return YES; // Migrated to the final model
  33. }
  34. @autoreleasepool {
  35. NSManagedObjectModel *sourceModel = self.models[modelIndex];
  36. NSManagedObjectModel *destinationModel = [self destinationModelForSourceModel:sourceModel sourceModelIndex:modelIndex];
  37. if (![self migrateFromSourceModel:sourceModel toDestinationModel:destinationModel error:error]) {
  38. return NO;
  39. }
  40. }
  41. return [self migrate:error];
  42. }
  43.  
  44. - (NSInteger)_indexOfModelCompatibleWithTheStore:(NSError **)error {
  45. NSDictionary *metadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:self.storeType URL:self.storeURL options:self.storeOptions error:error];
  46. return metadata ? [self.models indexOfObjectPassingTest:^BOOL(NSManagedObjectModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  47. return [obj isConfiguration:self.storeConfiguraton compatibleWithStoreMetadata:metadata];
  48. }] : NSNotFound;
  49. }
  50.  
  51. - (NSManagedObjectModel *)destinationModelForSourceModel:(NSManagedObjectModel *)sourceModel sourceModelIndex:(NSInteger)sourceModelIndex {
  52. if (sourceModelIndex == self.models.count + 1) {
  53. [NSException raise:NSInvalidArgumentException format:@"Attempting to find a destination model for the final model"];
  54. }
  55. return self.models[(sourceModelIndex + 1)];
  56. }
  57.  
  58. - (BOOL)migrateFromSourceModel:(NSManagedObjectModel *)sourceModel toDestinationModel:(NSManagedObjectModel *)destinationModel error:(NSError *__autoreleasing _Nullable *)error {
  59. NSArray<NSMappingModel *> *mappings = [self mappingModelsForSourceModel:sourceModel destinationModel:destinationModel error:error];
  60. if (!mappings.count) {
  61. return NO;
  62. }
  63.  
  64. // Create temp directory for destination store
  65. NSURL *tempDirectory = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:[NSUUID UUID].UUIDString];
  66. if (![[NSFileManager defaultManager] createDirectoryAtURL:tempDirectory withIntermediateDirectories:YES attributes:nil error:error]) {
  67. return NO;
  68. }
  69.  
  70. // Migrate store to temp destination
  71. NSURL *destinationURL = [tempDirectory URLByAppendingPathComponent:self.storeURL.lastPathComponent];
  72. NSMigrationManager *manager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel destinationModel:destinationModel];
  73. for (NSMappingModel *mapping in mappings) {
  74. @autoreleasepool {
  75. if (![manager migrateStoreFromURL:self.storeURL
  76. type:self.storeType
  77. options:self.storeOptions
  78. withMappingModel:mapping
  79. toDestinationURL:destinationURL
  80. destinationType:self.storeType
  81. destinationOptions:self.storeOptions
  82. error:error]) {
  83. return NO;
  84. }
  85. }
  86. }
  87.  
  88. // Remove source store and move destination store to source store location
  89. if (![self replaceStoreAtURL:self.storeURL withStoreFromURL:destinationURL error:error]) {
  90. return NO;
  91. }
  92.  
  93. // Cleanup
  94. [[NSFileManager defaultManager] removeItemAtURL:tempDirectory error:nil];
  95. return YES;
  96. }
  97.  
  98. - (NSArray<NSMappingModel *> *)mappingModelsForSourceModel:(NSManagedObjectModel *)sourceModel destinationModel:(NSManagedObjectModel *)destinationModel error:(NSError *__autoreleasing _Nullable *)error {
  99. // Find custom mapping
  100. NSMappingModel *mapping = [NSMappingModel mappingModelFromBundles:@[[NSBundle mainBundle]] forSourceModel:sourceModel destinationModel:destinationModel];
  101. if (!mapping) {
  102. // Find inferred mapping (lightweight migration)
  103. mapping = [NSMappingModel inferredMappingModelForSourceModel:sourceModel destinationModel:destinationModel error:error];
  104. }
  105. if (!mapping) {
  106. if (error != NULL && *error == nil) {
  107. *error = [NSError errorWithDomain:DFMigrationErrorDomain code:DFMigrationErrorMappingModelNotFound userInfo:@{ NSLocalizedDescriptionKey: @"Failed to find a mapping model" }];
  108. }
  109. }
  110. return mapping ? @[mapping] : nil;
  111. }
  112.  
  113. // FIXME: Write safer version. Check out `-replacePersistentStoreAtURL:destinationOptions:withPersistentStoreFromURL:sourceOptions:storeType:error:` method.
  114. - (BOOL)replaceStoreAtURL:(NSURL *)destinationURL withStoreFromURL:(NSURL *)sourceURL error:(NSError *__autoreleasing _Nullable *)error {
  115. // Remove all destination store's files, including -wal, -shm files, etc.
  116. for (NSURL *URL in [self _URLsForStoreURL:destinationURL]) {
  117. if (![[NSFileManager defaultManager] removeItemAtURL:URL error:error]) {
  118. return NO;
  119. }
  120. }
  121.  
  122. // Move source store's files to destination store location.
  123. NSURL *destinationDirectoryURL = destinationURL.URLByDeletingLastPathComponent;
  124. for (NSURL *fromURL in [self _URLsForStoreURL:sourceURL]) {
  125. NSURL *toURL = [destinationDirectoryURL URLByAppendingPathComponent:fromURL.lastPathComponent];
  126. if (![[NSFileManager defaultManager] moveItemAtURL:fromURL toURL:toURL error:error]) {
  127. return NO;
  128. }
  129. }
  130. return YES;
  131. }
  132.  
  133. - (NSArray<NSURL *> *)_URLsForStoreURL:(NSURL *)storeURL {
  134. NSURL *storeDirectoryURL = [storeURL URLByDeletingLastPathComponent];
  135. NSArray<NSURL *> *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:storeDirectoryURL includingPropertiesForKeys:nil options:kNilOptions error:nil];
  136. return [contents filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSURL * _Nonnull obj, NSDictionary<NSString *,id> * _Nullable bindings) {
  137. return [obj.lastPathComponent containsString:storeURL.lastPathComponent];
  138. }]];
  139. }
  140.  
  141. @end
  142.  
  143.  
  144. @implementation DFMigrationManager (Convenience)
  145.  
  146. + (BOOL)migrateStoreAtURL:(NSURL *)storeURL storeType:(NSString *)storeType orderedModels:(NSArray<NSManagedObjectModel *> *)models error:(NSError *__autoreleasing _Nullable *)error {
  147. return [[[DFMigrationManager alloc] initWithStoreURL:storeURL storeType:storeType orderedModels:models] migrate:error];
  148. }
  149.  
  150. @end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement