-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathTracksContextManager.m
150 lines (122 loc) · 7.15 KB
/
TracksContextManager.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
#import "TracksContextManager.h"
#import "TracksLogging.h"
NSString *const TracksApplicationSupportException = @"TracksApplicationSupportException";
NSString *const TracksPersistentStoreException = @"TracksPersistentStoreException";
@implementation TracksContextManager
#pragma mark - Core Data stack
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
/*
tracksBundle is the "bundle" containing the DataModel.bundle:
- If frameworks are used, it's Automattic-Tracks-iOS.framework
- Otherwise, it's the main bundle
*/
#if SWIFT_PACKAGE
NSBundle *bundle = SWIFTPM_MODULE_BUNDLE;
NSURL *modelURL = [bundle URLForResource:@"Tracks" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
#else
NSBundle *tracksBundle = [NSBundle bundleForClass:[self class]];
NSString *path = [tracksBundle pathForResource:@"DataModel" ofType:@"bundle"];
NSBundle *bundle = path != nil ? [NSBundle bundleWithPath:path] : [NSBundle bundleForClass:[self class]];
NSURL *modelURL = [bundle URLForResource:@"Tracks" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
#endif
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [self storeURL];
NSError *error = nil;
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// Delete the store and try again
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// This is not really an officially public way to check for prewarming, but it's the only way we have to
// check it, and since this information is extremely important for being able to debug issues here
// it's worth including it. Worst case scenario this could cease working, but should not cause any issues
// whatsoever.
NSString *prewarming = [NSProcessInfo processInfo].environment[@"ActivePrewarm"] ?: @"0";
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
TracksLogError(@"Unresolved error %@, %@. Context info {prewarming=%@, storeURL=%@}.", error, [error userInfo], prewarming, storeURL);
@throw [NSException exceptionWithName:TracksPersistentStoreException
reason:[NSString stringWithFormat:@"Error initializing Tracks: %@", error]
userInfo:error.userInfo];
}
}
return _persistentStoreCoordinator;
}
- (NSURL *)storeURL {
return [[self storeContainerDirectoryURL] URLByAppendingPathComponent:@"Tracks.sqlite"];
}
- (NSURL *)storeContainerDirectoryURL {
return [self applicationSupportURLForContainerApp];
}
- (NSURL *)applicationSupportURLForContainerApp {
// The container app is the one owning the main bundle
return [self applicationSupportURLForAppWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
}
- (NSURL *)applicationSupportURLForAppWithBundleIdentifier:(NSString *)bundleIdentifier {
NSURL *folder = [[self applicationSupportURL] URLByAppendingPathComponent:bundleIdentifier];
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtURL:folder
withIntermediateDirectories:true
attributes:nil
error:&error];
// It seems safe not to handle this error because Application Support should always be
// available and one should always be able to create a folder in it
if (error != nil) {
TracksLogError(@"Failed to create folder for %@ in Application Support: %@, %@", bundleIdentifier, error, [error userInfo]);
@throw [NSException exceptionWithName:TracksApplicationSupportException
reason:[NSString stringWithFormat:@"Error creating the ApplicationSupport Folder: %@", error]
userInfo:error.userInfo];
}
return folder;
}
// Application Support contains "the files that your app creates and manages on behalf of the user
// and can include files that contain user data".
//
// See:
// https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html#//apple_ref/doc/uid/TP40010672-CH10-SW1
- (NSURL *)applicationSupportURL {
// Application Support should always be available, so no checking whether the array is empty
return [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask] lastObject];
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
TracksLogError(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
@end