forked from hwaxxer/BookMigration
/
MHWCoreDataController.m
205 lines (169 loc) · 6.88 KB
/
MHWCoreDataController.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//
// MHWCoreDataController.m
// BookMigration
//
// Created by Martin Hwasser on 8/26/13.
// Copyright (c) 2013 Martin Hwasser. All rights reserved.
//
#import "MHWCoreDataController.h"
#import "NSFileManager+MHWAdditions.h"
#import "MHWMigrationManager.h"
#import "NSManagedObjectModel+MHWAdditions.h"
@interface MHWCoreDataController ()
@property (nonatomic, readwrite, strong) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, readwrite, strong) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, readwrite, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@end
@implementation MHWCoreDataController
+ (MHWCoreDataController *)sharedInstance
{
static MHWCoreDataController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [MHWCoreDataController new];
});
return sharedInstance;
}
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
_managedObjectContext = [[NSManagedObjectContext alloc] init];
_managedObjectContext.persistentStoreCoordinator = coordinator;
return _managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSString *momPath = [[NSBundle mainBundle] pathForResource:@"Model"
ofType:@"momd"];
if (!momPath) {
momPath = [[NSBundle mainBundle] pathForResource:@"Model"
ofType:@"mom"];
}
NSURL *url = [NSURL fileURLWithPath:momPath];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:url];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSError *error = nil;
NSDictionary *options = nil;
if ([self isMigrationNeeded]) {
options = @{
NSInferMappingModelAutomaticallyOption: @YES,
NSSQLitePragmasOption: @{@"journal_mode": @"DELETE"}
};
} else {
options = @{
NSInferMappingModelAutomaticallyOption: @YES,
NSSQLitePragmasOption: @{@"journal_mode": @"WAL"}
};
}
NSManagedObjectModel *mom = [self managedObjectModel];
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![_persistentStoreCoordinator addPersistentStoreWithType:[self sourceStoreType]
configuration:nil
URL:[self sourceStoreURL]
options:options
error:&error]) {
NSLog(@"error: %@", error);
NSFileManager *fileManager = [NSFileManager new];
[fileManager removeItemAtPath:[self sourceStoreURL].path error:nil];
[[[UIAlertView alloc] initWithTitle:@"Ouch"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
return _persistentStoreCoordinator;
}
- (NSURL *)sourceStoreURL
{
return [[NSFileManager urlToApplicationSupportDirectory] URLByAppendingPathComponent:@"Model.sqlite"];
}
- (NSString *)sourceStoreType
{
return NSSQLiteStoreType;
}
- (NSDictionary *)sourceMetadata:(NSError **)error
{
return [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:[self sourceStoreType]
URL:[self sourceStoreURL]
error:error];
}
- (BOOL)isMigrationNeeded
{
NSError *error = nil;
// Check if we need to migrate
NSDictionary *sourceMetadata = [self sourceMetadata:&error];
BOOL isMigrationNeeded = NO;
if (sourceMetadata != nil) {
NSManagedObjectModel *destinationModel = [self managedObjectModel];
// Migration is needed if destinationModel is NOT compatible
isMigrationNeeded = ![destinationModel isConfiguration:nil
compatibleWithStoreMetadata:sourceMetadata];
}
NSLog(@"isMigrationNeeded: %d", isMigrationNeeded);
return isMigrationNeeded;
}
- (BOOL)migrate:(NSError *__autoreleasing *)error
{
// Enable migrations to run even while user exits app
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
MHWMigrationManager *migrationManager = [MHWMigrationManager new];
migrationManager.delegate = self;
BOOL OK = [migrationManager progressivelyMigrateURL:[self sourceStoreURL]
ofType:[self sourceStoreType]
toModel:[self managedObjectModel]
error:error];
if (OK) {
NSLog(@"migration complete");
}
// Mark it as invalid
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
return OK;
}
#pragma mark -
#pragma mark - MHWMigrationManagerDelegate
- (void)migrationManager:(MHWMigrationManager *)migrationManager migrationProgress:(float)migrationProgress
{
NSLog(@"migration progress: %f", migrationProgress);
}
- (NSArray *)migrationManager:(MHWMigrationManager *)migrationManager
mappingModelsForSourceModel:(NSManagedObjectModel *)sourceModel
{
NSMutableArray *mappingModels = [@[] mutableCopy];
NSString *modelName = [sourceModel mhw_modelName];
if ([modelName isEqual:@"Model2"]) {
// Migrating to Model3
NSArray *urls = [[NSBundle bundleForClass:[self class]]
URLsForResourcesWithExtension:@"cdm"
subdirectory:nil];
for (NSURL *url in urls) {
if ([url.lastPathComponent rangeOfString:@"Model2_to_Model"].length != 0) {
NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:url];
if ([url.lastPathComponent rangeOfString:@"User"].length != 0) {
// User first so we create new relationship
[mappingModels insertObject:mappingModel atIndex:0];
} else {
[mappingModels addObject:mappingModel];
}
}
}
}
return mappingModels;
}
@end