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
@@ -40,25 +39,27 @@ - (instancetype)init
4039 YES ) lastObject ];
4140 NSURL *storeURL = [NSURL fileURLWithPath: [documentsDirectory stringByAppendingPathComponent: @" WordPress.sqlite" ]];
4241
43- return [self initWithModelName: ContextManagerModelNameCurrent storeURL: storeURL];
42+ return [self initWithModelName: ContextManagerModelNameCurrent storeURL: storeURL contextFactory: [LegacyContextFactory class ] ];
4443}
4544
46- - (instancetype )initWithModelName : (NSString *)modelName storeURL : (NSURL *)storeURL
45+ - (instancetype )initWithModelName : (NSString *)modelName storeURL : (NSURL *)storeURL contextFactory : ( Class ) factory
4746{
4847 self = [super init ];
4948 if (self) {
49+ if (factory == nil ) {
50+ factory = [LegacyContextFactory class ];
51+ }
52+
5053 NSParameterAssert ([modelName isEqualToString: ContextManagerModelNameCurrent] || [modelName hasPrefix: @" WordPress " ]);
5154 NSParameterAssert ([storeURL isFileURL ]);
55+ NSParameterAssert ([factory conformsToProtocol: @protocol (ManagedObjectContextFactory)]);
5256
5357 self.modelName = modelName;
5458 self.storeURL = storeURL;
5559
5660 [NSValueTransformer registerCustomTransformers ];
57- // Create `mainContext` and `writerContext` during initialisation to
58- // ensure they are only created once.
59- [self createWriterContext ];
60- [self createMainContext ];
61- [self startListeningToMainContextNotifications ];
61+ self.contextFactory = (id <ManagedObjectContextFactory>)[[factory alloc ] initWithPersistentContainer: self .persistentContainer];
62+ [[[NullBlogPropertySanitizer alloc ] initWithContext: self .contextFactory.mainContext] sanitize ];
6263 }
6364
6465 return self;
@@ -81,118 +82,31 @@ + (NSURL *)inMemoryStoreURL
8182
8283#pragma mark - Contexts
8384
84- - (NSManagedObjectContext *const )newDerivedContext
85- {
86- return [self newChildContextWithConcurrencyType: NSPrivateQueueConcurrencyType];
87- }
88-
89- - (void )createWriterContext
85+ - (NSManagedObjectContext *)mainContext
9086{
91- NSAssert (self.writerContext == nil , @" %s should only be called once" , __PRETTY_FUNCTION__);
92-
93- NSManagedObjectContext *context = [[NSManagedObjectContext alloc ] initWithConcurrencyType: NSPrivateQueueConcurrencyType];
94- context.persistentStoreCoordinator = self.persistentStoreCoordinator ;
95- self.writerContext = context;
87+ return self.contextFactory .mainContext ;
9688}
9789
98- - (void )createMainContext
99- {
100- NSAssert (self.mainContext == nil , @" %s should only be called once" , __PRETTY_FUNCTION__);
101-
102- NSManagedObjectContext *context = [[NSManagedObjectContext alloc ] initWithConcurrencyType: NSMainQueueConcurrencyType];
103- context.parentContext = self.writerContext ;
104- self.mainContext = context;
105- [[[NullBlogPropertySanitizer alloc ] initWithContext: context] sanitize ];
106- }
107-
108- - (NSManagedObjectContext *const )newChildContextWithConcurrencyType : (NSManagedObjectContextConcurrencyType )concurrencyType
90+ - (NSManagedObjectContext *const )newDerivedContext
10991{
110- NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc ]
111- initWithConcurrencyType: concurrencyType];
112- childContext.parentContext = self.mainContext ;
113- childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy ;
114-
115- return childContext;
92+ return [self .contextFactory newDerivedContext ];
11693}
11794
118-
11995#pragma mark - Context Saving and Merging
12096
12197- (void )saveContextAndWait : (NSManagedObjectContext *)context
12298{
123- [self saveContext: context andWait: YES withCompletionBlock: nil ];
99+ [self .contextFactory saveContext: context andWait: YES withCompletionBlock: nil ];
124100}
125101
126102- (void )saveContext : (NSManagedObjectContext *)context
127103{
128- [self saveContext: context andWait: NO withCompletionBlock: nil ];
104+ [self .contextFactory saveContext: context andWait: NO withCompletionBlock: nil ];
129105}
130106
131107- (void )saveContext : (NSManagedObjectContext *)context withCompletionBlock : (void (^)(void ))completionBlock
132108{
133- [self saveContext: context andWait: NO withCompletionBlock: completionBlock];
134- }
135-
136-
137- - (void )saveContext : (NSManagedObjectContext *)context andWait : (BOOL )wait withCompletionBlock : (void (^)(void ))completionBlock
138- {
139- // Save derived contexts a little differently
140- if (context.parentContext == self.mainContext ) {
141- [self saveDerivedContext: context andWait: wait withCompletionBlock: completionBlock];
142- return ;
143- }
144-
145- if (wait) {
146- [context performBlockAndWait: ^{
147- [self internalSaveContext: context withCompletionBlock: completionBlock];
148- }];
149- } else {
150- [context performBlock: ^{
151- [self internalSaveContext: context withCompletionBlock: completionBlock];
152- }];
153- }
154- }
155-
156- - (void )saveDerivedContext : (NSManagedObjectContext *)context andWait : (BOOL )wait withCompletionBlock : (void (^)(void ))completionBlock
157- {
158- if (wait) {
159- [context performBlockAndWait: ^{
160- [self internalSaveContext: context];
161- [self saveContext: self .mainContext andWait: wait withCompletionBlock: completionBlock];
162- }];
163- } else {
164- [context performBlock: ^{
165- [self internalSaveContext: context];
166- [self saveContext: self .mainContext andWait: wait withCompletionBlock: completionBlock];
167- }];
168- }
169- }
170-
171- - (void )internalSaveContext : (NSManagedObjectContext *)context withCompletionBlock : (void (^)(void ))completionBlock
172- {
173- [self internalSaveContext: context];
174-
175- if (completionBlock) {
176- dispatch_async (dispatch_get_main_queue (), completionBlock);
177- }
178- }
179-
180- - (void )mergeChanges : (NSManagedObjectContext *)context fromContextDidSaveNotification : (NSNotification *)notification
181- {
182- [context performBlock: ^{
183- // Fault-in updated objects before a merge to avoid any internal inconsistency errors later.
184- // Based on old solution referenced here: http://www.mlsite.net/blog/?p=518
185- NSSet * updates = [notification.userInfo objectForKey: NSUpdatedObjectsKey ];
186- for (NSManagedObject *object in updates) {
187- NSManagedObject *objectInContext = [context existingObjectWithID: object.objectID error: nil ];
188- if ([objectInContext isFault ]) {
189- // Force a fault-in of the object's key-values
190- [objectInContext willAccessValueForKey: nil ];
191- }
192- }
193- // Continue with the merge
194- [context mergeChangesFromContextDidSaveNotification: notification];
195- }];
109+ [self .contextFactory saveContext: context andWait: NO withCompletionBlock: completionBlock];
196110}
197111
198112- (void )saveUsingBlock : (void (^)(NSManagedObjectContext *context))aBlock
@@ -211,7 +125,7 @@ - (void)saveUsingBlock:(void (^)(NSManagedObjectContext *context))aBlock complet
211125 [context performBlock: ^{
212126 aBlock (context);
213127
214- [self saveContext: context andWait: NO withCompletionBlock: completion];
128+ [self .contextFactory saveContext: context andWait: NO withCompletionBlock: completion];
215129 }];
216130}
217131
@@ -294,46 +208,9 @@ - (NSManagedObjectModel *)managedObjectModel
294208 return _managedObjectModel;
295209}
296210
297- - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
298- {
299- return self.persistentContainer .persistentStoreCoordinator ;
300- }
301-
302-
303- #pragma mark - Notification Helpers
304-
305- - (void )startListeningToMainContextNotifications
306- {
307- NSNotificationCenter *nc = [NSNotificationCenter defaultCenter ];
308- [nc addObserver: self selector: @selector (mainContextDidSave: ) name: NSManagedObjectContextDidSaveNotification object: self .mainContext];
309- }
310-
311- - (void )mainContextDidSave : (NSNotification *)notification
312- {
313- // Defer I/O to a BG Writer Context. Simperium 4ever!
314- //
315- [self .writerContext performBlock: ^{
316- [self internalSaveContext: self .writerContext];
317- }];
318- }
319-
320211
321212#pragma mark - Private Helpers
322213
323- - (void )internalSaveContext : (NSManagedObjectContext *)context
324- {
325- NSParameterAssert (context);
326-
327- NSError *error;
328- if (![context obtainPermanentIDsForObjects: context.insertedObjects.allObjects error: &error]) {
329- DDLogError (@" Error obtaining permanent object IDs for %@ , %@ " , context.insertedObjects .allObjects , error);
330- }
331-
332- if ([context hasChanges ] && ![context save: &error]) {
333- [self handleSaveError: error inContext: context];
334- }
335- }
336-
337214- (void )migrateDataModelsIfNecessary : (SentryStartupEvent *)sentryEvent
338215{
339216 if (![[NSFileManager defaultManager ] fileExistsAtPath: [[self storeURL ] path ]]) {
0 commit comments