11#import " ContextManager.h"
2+ #import " LegacyContextFactory.h"
23#import " WordPress-Swift.h"
34@import WordPressShared.WPAnalytics;
45@import Foundation;
@@ -18,13 +19,11 @@ @interface ContextManager ()
1819
1920@property (nonatomic , strong ) NSPersistentStoreDescription *storeDescription;
2021@property (nonatomic , strong ) NSPersistentContainer *persistentContainer;
21- @property (nonatomic , strong ) NSPersistentStoreCoordinator *persistentStoreCoordinator;
2222@property (nonatomic , strong ) NSManagedObjectModel *managedObjectModel;
23- @property (nonatomic , strong ) NSManagedObjectContext *mainContext;
24- @property (nonatomic , strong ) NSManagedObjectContext *writerContext;
2523@property (nonatomic , assign ) BOOL migrationFailed;
2624@property (nonatomic , strong ) NSString *modelName;
2725@property (nonatomic , strong ) NSURL *storeURL;
26+ @property (nonatomic , strong ) id <ManagedObjectContextFactory> contextFactory;
2827
2928@end
3029
@@ -43,25 +42,27 @@ - (instancetype)init
4342 storeURL = [self localDatabasePath ];
4443 }
4544
46- return [self initWithModelName: ContextManagerModelNameCurrent storeURL: storeURL];
45+ return [self initWithModelName: ContextManagerModelNameCurrent storeURL: storeURL contextFactory: nil ];
4746}
4847
49- - (instancetype )initWithModelName : (NSString *)modelName storeURL : (NSURL *)storeURL
48+ - (instancetype )initWithModelName : (NSString *)modelName storeURL : (NSURL *)storeURL contextFactory : ( Class ) factory
5049{
5150 self = [super init ];
5251 if (self) {
52+ if (factory == nil ) {
53+ factory = [ContainerContextFactory class ];
54+ }
55+
5356 NSParameterAssert ([modelName isEqualToString: ContextManagerModelNameCurrent] || [modelName hasPrefix: @" WordPress " ]);
5457 NSParameterAssert ([storeURL isFileURL ]);
58+ NSParameterAssert ([factory conformsToProtocol: @protocol (ManagedObjectContextFactory)]);
5559
5660 self.modelName = modelName;
5761 self.storeURL = storeURL;
5862
5963 [NSValueTransformer registerCustomTransformers ];
60- // Create `mainContext` and `writerContext` during initialisation to
61- // ensure they are only created once.
62- [self createWriterContext ];
63- [self createMainContext ];
64- [self startListeningToMainContextNotifications ];
64+ self.contextFactory = (id <ManagedObjectContextFactory>)[[factory alloc ] initWithPersistentContainer: self .persistentContainer];
65+ [[[NullBlogPropertySanitizer alloc ] initWithContext: self .contextFactory.mainContext] sanitize ];
6566 }
6667
6768 return self;
@@ -84,118 +85,31 @@ + (NSURL *)inMemoryStoreURL
8485
8586#pragma mark - Contexts
8687
87- - (NSManagedObjectContext *const )newDerivedContext
88- {
89- return [self newChildContextWithConcurrencyType: NSPrivateQueueConcurrencyType];
90- }
91-
92- - (void )createWriterContext
88+ - (NSManagedObjectContext *)mainContext
9389{
94- NSAssert (self.writerContext == nil , @" %s should only be called once" , __PRETTY_FUNCTION__);
95-
96- NSManagedObjectContext *context = [[NSManagedObjectContext alloc ] initWithConcurrencyType: NSPrivateQueueConcurrencyType];
97- context.persistentStoreCoordinator = self.persistentStoreCoordinator ;
98- self.writerContext = context;
90+ return self.contextFactory .mainContext ;
9991}
10092
101- - (void )createMainContext
102- {
103- NSAssert (self.mainContext == nil , @" %s should only be called once" , __PRETTY_FUNCTION__);
104-
105- NSManagedObjectContext *context = [[NSManagedObjectContext alloc ] initWithConcurrencyType: NSMainQueueConcurrencyType];
106- context.parentContext = self.writerContext ;
107- self.mainContext = context;
108- [[[NullBlogPropertySanitizer alloc ] initWithContext: context] sanitize ];
109- }
110-
111- - (NSManagedObjectContext *const )newChildContextWithConcurrencyType : (NSManagedObjectContextConcurrencyType )concurrencyType
93+ - (NSManagedObjectContext *const )newDerivedContext
11294{
113- NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc ]
114- initWithConcurrencyType: concurrencyType];
115- childContext.parentContext = self.mainContext ;
116- childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy ;
117-
118- return childContext;
95+ return [self .contextFactory newDerivedContext ];
11996}
12097
121-
12298#pragma mark - Context Saving and Merging
12399
124100- (void )saveContextAndWait : (NSManagedObjectContext *)context
125101{
126- [self saveContext: context andWait: YES withCompletionBlock: nil ];
102+ [self .contextFactory saveContext: context andWait: YES withCompletionBlock: nil ];
127103}
128104
129105- (void )saveContext : (NSManagedObjectContext *)context
130106{
131- [self saveContext: context andWait: NO withCompletionBlock: nil ];
107+ [self .contextFactory saveContext: context andWait: NO withCompletionBlock: nil ];
132108}
133109
134110- (void )saveContext : (NSManagedObjectContext *)context withCompletionBlock : (void (^)(void ))completionBlock
135111{
136- [self saveContext: context andWait: NO withCompletionBlock: completionBlock];
137- }
138-
139-
140- - (void )saveContext : (NSManagedObjectContext *)context andWait : (BOOL )wait withCompletionBlock : (void (^)(void ))completionBlock
141- {
142- // Save derived contexts a little differently
143- if (context.parentContext == self.mainContext ) {
144- [self saveDerivedContext: context andWait: wait withCompletionBlock: completionBlock];
145- return ;
146- }
147-
148- if (wait) {
149- [context performBlockAndWait: ^{
150- [self internalSaveContext: context withCompletionBlock: completionBlock];
151- }];
152- } else {
153- [context performBlock: ^{
154- [self internalSaveContext: context withCompletionBlock: completionBlock];
155- }];
156- }
157- }
158-
159- - (void )saveDerivedContext : (NSManagedObjectContext *)context andWait : (BOOL )wait withCompletionBlock : (void (^)(void ))completionBlock
160- {
161- if (wait) {
162- [context performBlockAndWait: ^{
163- [self internalSaveContext: context];
164- [self saveContext: self .mainContext andWait: wait withCompletionBlock: completionBlock];
165- }];
166- } else {
167- [context performBlock: ^{
168- [self internalSaveContext: context];
169- [self saveContext: self .mainContext andWait: wait withCompletionBlock: completionBlock];
170- }];
171- }
172- }
173-
174- - (void )internalSaveContext : (NSManagedObjectContext *)context withCompletionBlock : (void (^)(void ))completionBlock
175- {
176- [self internalSaveContext: context];
177-
178- if (completionBlock) {
179- dispatch_async (dispatch_get_main_queue (), completionBlock);
180- }
181- }
182-
183- - (void )mergeChanges : (NSManagedObjectContext *)context fromContextDidSaveNotification : (NSNotification *)notification
184- {
185- [context performBlock: ^{
186- // Fault-in updated objects before a merge to avoid any internal inconsistency errors later.
187- // Based on old solution referenced here: http://www.mlsite.net/blog/?p=518
188- NSSet * updates = [notification.userInfo objectForKey: NSUpdatedObjectsKey ];
189- for (NSManagedObject *object in updates) {
190- NSManagedObject *objectInContext = [context existingObjectWithID: object.objectID error: nil ];
191- if ([objectInContext isFault ]) {
192- // Force a fault-in of the object's key-values
193- [objectInContext willAccessValueForKey: nil ];
194- }
195- }
196- // Continue with the merge
197- [context mergeChangesFromContextDidSaveNotification: notification];
198- }];
112+ [self .contextFactory saveContext: context andWait: NO withCompletionBlock: completionBlock];
199113}
200114
201115- (void )performAndSaveUsingBlock : (void (^)(NSManagedObjectContext *context))aBlock
@@ -214,7 +128,7 @@ - (void)performAndSaveUsingBlock:(void (^)(NSManagedObjectContext *context))aBlo
214128 [context performBlock: ^{
215129 aBlock (context);
216130
217- [self saveContext: context andWait: NO withCompletionBlock: completion];
131+ [self .contextFactory saveContext: context andWait: NO withCompletionBlock: completion];
218132 }];
219133}
220134
@@ -297,46 +211,9 @@ - (NSManagedObjectModel *)managedObjectModel
297211 return _managedObjectModel;
298212}
299213
300- - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
301- {
302- return self.persistentContainer .persistentStoreCoordinator ;
303- }
304-
305-
306- #pragma mark - Notification Helpers
307-
308- - (void )startListeningToMainContextNotifications
309- {
310- NSNotificationCenter *nc = [NSNotificationCenter defaultCenter ];
311- [nc addObserver: self selector: @selector (mainContextDidSave: ) name: NSManagedObjectContextDidSaveNotification object: self .mainContext];
312- }
313-
314- - (void )mainContextDidSave : (NSNotification *)notification
315- {
316- // Defer I/O to a BG Writer Context. Simperium 4ever!
317- //
318- [self .writerContext performBlock: ^{
319- [self internalSaveContext: self .writerContext];
320- }];
321- }
322-
323214
324215#pragma mark - Private Helpers
325216
326- - (void )internalSaveContext : (NSManagedObjectContext *)context
327- {
328- NSParameterAssert (context);
329-
330- NSError *error;
331- if (![context obtainPermanentIDsForObjects: context.insertedObjects.allObjects error: &error]) {
332- DDLogError (@" Error obtaining permanent object IDs for %@ , %@ " , context.insertedObjects .allObjects , error);
333- }
334-
335- if ([context hasChanges ] && ![context save: &error]) {
336- [self handleSaveError: error inContext: context];
337- }
338- }
339-
340217- (void )migrateDataModelsIfNecessary : (SentryStartupEvent *)sentryEvent
341218{
342219 if (![[NSFileManager defaultManager ] fileExistsAtPath: [[self storeURL ] path ]]) {
0 commit comments