diff --git a/WordPress/Classes/Categories/Media+WPMediaAsset.m b/WordPress/Classes/Categories/Media+WPMediaAsset.m index 43dc6c8803ad..cf8b06fc6c5f 100644 --- a/WordPress/Classes/Categories/Media+WPMediaAsset.m +++ b/WordPress/Classes/Categories/Media+WPMediaAsset.m @@ -1,7 +1,7 @@ #import "Media+WPMediaAsset.h" #import "MediaService.h" #import "Blog.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WordPress-Swift.h" @implementation Media(WPMediaAsset) diff --git a/WordPress/Classes/Models/AbstractPost.m b/WordPress/Classes/Models/AbstractPost.m index f05762f90256..1e62605b84c2 100644 --- a/WordPress/Classes/Models/AbstractPost.m +++ b/WordPress/Classes/Models/AbstractPost.m @@ -1,6 +1,6 @@ #import "AbstractPost.h" #import "Media.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WordPress-Swift.h" #import "BasePost.h" @import WordPressKit; diff --git a/WordPress/Classes/Models/BasePost.m b/WordPress/Classes/Models/BasePost.m index 9aef660f5c08..30ec2716066a 100644 --- a/WordPress/Classes/Models/BasePost.m +++ b/WordPress/Classes/Models/BasePost.m @@ -1,7 +1,7 @@ #import "BasePost.h" #import "Media.h" #import "NSMutableDictionary+Helpers.h" -#import "ContextManager.h" +#import "CoreDataStack.h" @import WordPressShared; @implementation BasePost diff --git a/WordPress/Classes/Models/Blog.m b/WordPress/Classes/Models/Blog.m index 1fb6e2ec0a7a..d6f885f4d70c 100644 --- a/WordPress/Classes/Models/Blog.m +++ b/WordPress/Classes/Models/Blog.m @@ -2,7 +2,7 @@ #import "WPAccount.h" #import "AccountService.h" #import "NSURL+IDN.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "Constants.h" #import "WordPress-Swift.h" #import "WPUserAgent.h" diff --git a/WordPress/Classes/Models/Media.m b/WordPress/Classes/Models/Media.m index 38320bc61f57..c6bd5de908bd 100644 --- a/WordPress/Classes/Models/Media.m +++ b/WordPress/Classes/Models/Media.m @@ -1,5 +1,5 @@ #import "Media.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WordPress-Swift.h" @implementation Media diff --git a/WordPress/Classes/Models/ReaderPost.m b/WordPress/Classes/Models/ReaderPost.m index 71fe3cef3b7b..ffd9bea01406 100644 --- a/WordPress/Classes/Models/ReaderPost.m +++ b/WordPress/Classes/Models/ReaderPost.m @@ -1,6 +1,6 @@ #import "ReaderPost.h" #import "AccountService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "SourcePostAttribution.h" #import "WPAccount.h" #import "WPAvatarSource.h" diff --git a/WordPress/Classes/Models/Theme.m b/WordPress/Classes/Models/Theme.m index 61c45381a421..d8e3586fed76 100644 --- a/WordPress/Classes/Models/Theme.m +++ b/WordPress/Classes/Models/Theme.m @@ -1,6 +1,6 @@ #import "Theme.h" #import "Blog.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WPAccount.h" #import "AccountService.h" #import "WordPress-Swift.h" diff --git a/WordPress/Classes/Services/AccountService.m b/WordPress/Classes/Services/AccountService.m index d7995f84d11b..38774d10a4a1 100644 --- a/WordPress/Classes/Services/AccountService.m +++ b/WordPress/Classes/Services/AccountService.m @@ -1,6 +1,6 @@ #import "AccountService.h" #import "WPAccount.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "Blog.h" #import "BlogService.h" #import "TodayExtensionService.h" diff --git a/WordPress/Classes/Services/BlogService.m b/WordPress/Classes/Services/BlogService.m index 66be2e716228..7bed0a5757e9 100644 --- a/WordPress/Classes/Services/BlogService.m +++ b/WordPress/Classes/Services/BlogService.m @@ -2,14 +2,13 @@ #import "Blog.h" #import "WPAccount.h" #import "AccountService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WPError.h" #import "Media.h" #import "PostCategoryService.h" #import "CommentService.h" #import "PostService.h" #import "TodayExtensionService.h" -#import "ContextManager.h" #import "WordPress-Swift.h" #import "PostType.h" @import WordPressKit; diff --git a/WordPress/Classes/Services/BlogSyncFacade.m b/WordPress/Classes/Services/BlogSyncFacade.m index 2519504dd432..79b4e072bc1b 100644 --- a/WordPress/Classes/Services/BlogSyncFacade.m +++ b/WordPress/Classes/Services/BlogSyncFacade.m @@ -1,5 +1,5 @@ #import "BlogSyncFacade.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "BlogService.h" #import "AccountService.h" #import "Blog.h" diff --git a/WordPress/Classes/Services/CommentService.m b/WordPress/Classes/Services/CommentService.m index bf45ff92512c..3422a783ad31 100644 --- a/WordPress/Classes/Services/CommentService.m +++ b/WordPress/Classes/Services/CommentService.m @@ -1,7 +1,7 @@ #import "CommentService.h" #import "AccountService.h" #import "Blog.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "ReaderPost.h" #import "WPAccount.h" #import "PostService.h" diff --git a/WordPress/Classes/Services/MediaService.m b/WordPress/Classes/Services/MediaService.m index 39a4b57d161f..33f08583e4e4 100644 --- a/WordPress/Classes/Services/MediaService.m +++ b/WordPress/Classes/Services/MediaService.m @@ -2,7 +2,7 @@ #import "AccountService.h" #import "Media.h" #import "WPAccount.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "Blog.h" #import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/Services/MenusService.m b/WordPress/Classes/Services/MenusService.m index 95f9e7dd8b93..32813551494e 100644 --- a/WordPress/Classes/Services/MenusService.m +++ b/WordPress/Classes/Services/MenusService.m @@ -4,7 +4,7 @@ #import "Menu.h" #import "MenuItem.h" #import "MenuLocation.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "PostService.h" #import "WordPress-Swift.h" @import WordPressKit; diff --git a/WordPress/Classes/Services/PostCategoryService.m b/WordPress/Classes/Services/PostCategoryService.m index d37ec17aeef0..d26b379960d5 100644 --- a/WordPress/Classes/Services/PostCategoryService.m +++ b/WordPress/Classes/Services/PostCategoryService.m @@ -1,7 +1,7 @@ #import "PostCategoryService.h" #import "PostCategory.h" #import "Blog.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WordPress-Swift.h" @import WordPressKit; diff --git a/WordPress/Classes/Services/PostService.m b/WordPress/Classes/Services/PostService.m index f39c3d0d1f51..1d687fa3014c 100644 --- a/WordPress/Classes/Services/PostService.m +++ b/WordPress/Classes/Services/PostService.m @@ -2,7 +2,7 @@ #import "Coordinate.h" #import "PostCategory.h" #import "PostCategoryService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "CommentService.h" #import "MediaService.h" #import "Media.h" diff --git a/WordPress/Classes/Services/PostTagService.m b/WordPress/Classes/Services/PostTagService.m index c40a7bbeb16b..552df40fd07b 100644 --- a/WordPress/Classes/Services/PostTagService.m +++ b/WordPress/Classes/Services/PostTagService.m @@ -1,7 +1,7 @@ #import "PostTagService.h" #import "Blog.h" #import "PostTag.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WordPress-Swift.h" @import WordPressKit; diff --git a/WordPress/Classes/Services/ReaderPostService.m b/WordPress/Classes/Services/ReaderPostService.m index cc62e9ea57ae..d61cf40f962d 100644 --- a/WordPress/Classes/Services/ReaderPostService.m +++ b/WordPress/Classes/Services/ReaderPostService.m @@ -1,7 +1,7 @@ #import "ReaderPostService.h" #import "AccountService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "ReaderGapMarker.h" #import "ReaderPost.h" #import "ReaderSiteService.h" diff --git a/WordPress/Classes/Services/ReaderSiteService.m b/WordPress/Classes/Services/ReaderSiteService.m index efa4fbd5dfae..1fcc5ac1eefe 100644 --- a/WordPress/Classes/Services/ReaderSiteService.m +++ b/WordPress/Classes/Services/ReaderSiteService.m @@ -1,7 +1,7 @@ #import "ReaderSiteService.h" #import "AccountService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "ReaderPostService.h" #import "ReaderPost.h" #import "WPAccount.h" diff --git a/WordPress/Classes/Services/ReaderTopicService.m b/WordPress/Classes/Services/ReaderTopicService.m index 6bf7bd1dff82..c7bcdf0b8f84 100644 --- a/WordPress/Classes/Services/ReaderTopicService.m +++ b/WordPress/Classes/Services/ReaderTopicService.m @@ -1,7 +1,7 @@ #import "ReaderTopicService.h" #import "AccountService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "ReaderPost.h" #import "ReaderPostService.h" #import "WPAccount.h" diff --git a/WordPress/Classes/Services/ThemeService.m b/WordPress/Classes/Services/ThemeService.m index 17c966e42e84..ecd62ac00df3 100644 --- a/WordPress/Classes/Services/ThemeService.m +++ b/WordPress/Classes/Services/ThemeService.m @@ -3,7 +3,7 @@ #import "Blog.h" #import "Theme.h" #import "WPAccount.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WordPress-Swift.h" @import WordPressKit; diff --git a/WordPress/Classes/System/WordPress-Bridging-Header.h b/WordPress/Classes/System/WordPress-Bridging-Header.h index 3ac17b6e238b..46dc717a14e1 100644 --- a/WordPress/Classes/System/WordPress-Bridging-Header.h +++ b/WordPress/Classes/System/WordPress-Bridging-Header.h @@ -21,13 +21,12 @@ #import "ConfigurablePostView.h" #import "Confirmable.h" #import "Constants.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "Coordinate.h" #import "CustomHighlightButton.h" #import "EditCommentViewController.h" -#import "LegacyContextFactory.h" #import "LocalCoreDataService.h" #import "Media.h" diff --git a/WordPress/Classes/Utility/Analytics/WPAnalyticsTrackerAutomatticTracks.m b/WordPress/Classes/Utility/Analytics/WPAnalyticsTrackerAutomatticTracks.m index 536f29d16c2c..88dd09224f9c 100644 --- a/WordPress/Classes/Utility/Analytics/WPAnalyticsTrackerAutomatticTracks.m +++ b/WordPress/Classes/Utility/Analytics/WPAnalyticsTrackerAutomatticTracks.m @@ -1,5 +1,5 @@ #import "WPAnalyticsTrackerAutomatticTracks.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "AccountService.h" #import "BlogService.h" #import "WPAccount.h" diff --git a/WordPress/Classes/Utility/Analytics/WPAppAnalytics.m b/WordPress/Classes/Utility/Analytics/WPAppAnalytics.m index b03db8087487..ca3c167eb683 100644 --- a/WordPress/Classes/Utility/Analytics/WPAppAnalytics.m +++ b/WordPress/Classes/Utility/Analytics/WPAppAnalytics.m @@ -1,6 +1,6 @@ #import "WPAppAnalytics.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WPAnalyticsTrackerWPCom.h" #import "WPAnalyticsTrackerAutomatticTracks.h" #import "WPTabBarController.h" diff --git a/WordPress/Classes/Utility/ContainerContextFactory.swift b/WordPress/Classes/Utility/ContainerContextFactory.swift deleted file mode 100644 index 350b8e543a18..000000000000 --- a/WordPress/Classes/Utility/ContainerContextFactory.swift +++ /dev/null @@ -1,44 +0,0 @@ -import ObjectiveC - -@objc -class ContainerContextFactory: NSObject, ManagedObjectContextFactory { - - private let container: NSPersistentContainer - - let mainContext: NSManagedObjectContext - - required init(persistentContainer container: NSPersistentContainer) { - self.container = container - self.mainContext = container.viewContext - self.mainContext.automaticallyMergesChangesFromParent = true - self.mainContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy - } - - func newDerivedContext() -> NSManagedObjectContext { - let context = container.newBackgroundContext() - context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy - return context - } - - func save(_ context: NSManagedObjectContext, andWait wait: Bool, withCompletionBlock completionBlock: (() -> Void)?) { - let block: () -> Void = { - self.internalSave(context) - DispatchQueue.main.async { - completionBlock?() - } - } - - /// Ensure that the `context`'s concurrency type is not `confinementConcurrencyType`, since it will crash if `perform` or `performAndWait` is called. - guard context.concurrencyType == .mainQueueConcurrencyType || context.concurrencyType == .privateQueueConcurrencyType else { - block() - return - } - - if wait { - context.performAndWait(block) - } else { - context.perform(block) - } - } - -} diff --git a/WordPress/Classes/Utility/ContextManager+ErrorHandling.swift b/WordPress/Classes/Utility/ContextManager+ErrorHandling.swift index 784a0349fb18..9d73ee139845 100644 --- a/WordPress/Classes/Utility/ContextManager+ErrorHandling.swift +++ b/WordPress/Classes/Utility/ContextManager+ErrorHandling.swift @@ -55,16 +55,7 @@ private extension NSExceptionName { static let coreDataSaveDerivedException = NSExceptionName("Unresolved Core Data save error (Derived Context)") } -extension LegacyContextFactory { - - /// A wrapper of `internalSave(_:)` to expose it as an Objective-C API for `LegacyContextFactory` to call. - @objc func internalSaveContext(_ context: NSManagedObjectContext) { - internalSave(context) - } - -} - -extension ManagedObjectContextFactory { +extension ContextManager { func internalSave(_ context: NSManagedObjectContext) { guard context.hasChanges else { @@ -87,7 +78,7 @@ extension ManagedObjectContextFactory { } -private extension ManagedObjectContextFactory { +private extension ContextManager { func handleSaveError(_ error: NSError, in context: NSManagedObjectContext) { let isMainContext = context == mainContext diff --git a/WordPress/Classes/Utility/ContextManager.h b/WordPress/Classes/Utility/ContextManager.h deleted file mode 100644 index 2cca69541dff..000000000000 --- a/WordPress/Classes/Utility/ContextManager.h +++ /dev/null @@ -1,107 +0,0 @@ -#import -#import - -#import "ManagedObjectContextFactory.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - A constant representing the current version of the data model. - - @see -[ContextManager initWithModelName:storeURL:] - */ -FOUNDATION_EXTERN NSString * const ContextManagerModelNameCurrent; - -@protocol CoreDataStack -@property (nonatomic, readonly, strong) NSManagedObjectContext *mainContext; -- (NSManagedObjectContext *const)newDerivedContext DEPRECATED_MSG_ATTRIBUTE("Use `performAndSave` instead"); -- (void)saveContextAndWait:(NSManagedObjectContext *)context; -- (void)saveContext:(NSManagedObjectContext *)context; -- (void)saveContext:(NSManagedObjectContext *)context withCompletionBlock:(void (^)(void))completionBlock; -- (void)performAndSaveUsingBlock:(void (^)(NSManagedObjectContext *context))aBlock; -- (void)performAndSaveUsingBlock:(void (^)(NSManagedObjectContext *context))aBlock completion:(void (^)(void))completion; -@end - -@interface ContextManager : NSObject - -/** - The URL for creating an in-memory database. - - @see -[ContextManager initWithModelName:storeURL:] - */ -@property (class, nonatomic, readonly) NSURL *inMemoryStoreURL; - -///---------------------------------------------- -///@name Persistent Contexts -/// -/// The mainContext has concurrency type NSMainQueueConcurrencyType and should be used -/// for UI elements and fetched results controllers. -/// Internally, we'll use a privateQueued context to perform disk write Operations. -/// -///---------------------------------------------- -@property (nonatomic, readonly, strong) NSManagedObjectContext *mainContext; - -///-------------------------------------- -///@name ContextManager -///-------------------------------------- - -/** - Returns the singleton - - @return instance of ContextManager -*/ -+ (instancetype)internalSharedInstance; - -/** - Create a ContextManager instance with given model name and database location. - - Note: This initialiser is only used for testing purpose at the moment. - - @param modelName Model name in Core Data data model file. - Use ContextManagerModelNameCurrent for current version, or - "WordPress " for specific version. - @param storeURL Database location. Use +[ContextManager inMemoryStoreURL] to create an in-memory database. - @param contextFactory A type that conforms to `ManagedObjectContextFactory`. - */ -- (instancetype)initWithModelName:(NSString *)modelName storeURL:(NSURL *)storeURL contextFactory:(Class _Nullable)factory NS_DESIGNATED_INITIALIZER; - - -///-------------------------- -///@name Contexts -///-------------------------- - -/** - For usage as a 'scratch pad' context or for doing background work. - - Make sure to save using saveDerivedContext: - - @return a new MOC with NSPrivateQueueConcurrencyType, - with the parent context as the main context -*/ -- (NSManagedObjectContext *const)newDerivedContext; - -/** - Save a given context synchronously. - - @param a NSManagedObject context instance - */ -- (void)saveContextAndWait:(NSManagedObjectContext *)context; - -/** - Save a given context. Convenience for error handling. - - @param a NSManagedObject context instance - */ -- (void)saveContext:(NSManagedObjectContext *)context; - -/** - Save a given context. - - @param a NSManagedObject context instance - @param a completion block that will be executed on the main queue - */ -- (void)saveContext:(NSManagedObjectContext *)context withCompletionBlock:(void (^)(void))completionBlock; - -@end - -NS_ASSUME_NONNULL_END diff --git a/WordPress/Classes/Utility/ContextManager.m b/WordPress/Classes/Utility/ContextManager.m deleted file mode 100644 index 593229654f90..000000000000 --- a/WordPress/Classes/Utility/ContextManager.m +++ /dev/null @@ -1,265 +0,0 @@ -#import "ContextManager.h" -#import "LegacyContextFactory.h" -#import "WordPress-Swift.h" -@import WordPressShared.WPAnalytics; -@import Foundation; - -#define SentryStartupEventAddError(event, error) [event addError:error file:__FILE__ function:__FUNCTION__ line:__LINE__] - -NSString * const ContextManagerModelNameCurrent = @"$CURRENT"; - -// MARK: - Static Variables -// -static ContextManager *_instance; - - -// MARK: - Private Properties -// -@interface ContextManager () - -@property (nonatomic, strong) NSPersistentStoreDescription *storeDescription; -@property (nonatomic, strong) NSPersistentContainer *persistentContainer; -@property (nonatomic, strong) NSManagedObjectModel *managedObjectModel; -@property (nonatomic, assign) BOOL migrationFailed; -@property (nonatomic, strong) NSString *modelName; -@property (nonatomic, strong) NSURL *storeURL; -@property (nonatomic, strong) id contextFactory; - -@end - - -// MARK: - ContextManager -// -@implementation ContextManager - -- (instancetype)init -{ - NSURL *storeURL = [self localDatabasePath]; - return [self initWithModelName:ContextManagerModelNameCurrent storeURL:storeURL contextFactory:nil]; -} - -- (instancetype)initWithModelName:(NSString *)modelName storeURL:(NSURL *)storeURL contextFactory:(Class)factory -{ - self = [super init]; - if (self) { - if (factory == nil) { - factory = [Feature enabled:FeatureFlagNewCoreDataContext] ? [ContainerContextFactory class] : [LegacyContextFactory class]; - } - - NSParameterAssert([modelName isEqualToString:ContextManagerModelNameCurrent] || [modelName hasPrefix:@"WordPress "]); - NSParameterAssert([storeURL isFileURL]); - NSParameterAssert([factory conformsToProtocol:@protocol(ManagedObjectContextFactory)]); - - self.modelName = modelName; - self.storeURL = storeURL; - - [NSValueTransformer registerCustomTransformers]; - self.contextFactory = (id)[[factory alloc] initWithPersistentContainer:self.persistentContainer]; - [[[NullBlogPropertySanitizer alloc] initWithContext:self.contextFactory.mainContext] sanitize]; - } - - return self; -} - -+ (instancetype)internalSharedInstance -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _instance = [[ContextManager alloc] init]; - }); - - return _instance; -} - -+ (NSURL *)inMemoryStoreURL -{ - return [NSURL fileURLWithPath:@"/dev/null"]; -} - -#pragma mark - Contexts - -- (NSManagedObjectContext *)mainContext -{ - return self.contextFactory.mainContext; -} - -- (NSManagedObjectContext *const)newDerivedContext -{ - return [self.contextFactory newDerivedContext]; -} - -#pragma mark - Context Saving and Merging - -- (void)saveContextAndWait:(NSManagedObjectContext *)context -{ - [self.contextFactory saveContext:context andWait:YES withCompletionBlock:nil]; -} - -- (void)saveContext:(NSManagedObjectContext *)context -{ - [self.contextFactory saveContext:context andWait:NO withCompletionBlock:nil]; -} - -- (void)saveContext:(NSManagedObjectContext *)context withCompletionBlock:(void (^)(void))completionBlock -{ - [self.contextFactory saveContext:context andWait:NO withCompletionBlock:completionBlock]; -} - -- (void)performAndSaveUsingBlock:(void (^)(NSManagedObjectContext *context))aBlock -{ - NSManagedObjectContext *context = [self newDerivedContext]; - [context performBlockAndWait:^{ - aBlock(context); - - [self saveContextAndWait:context]; - }]; -} - -- (void)performAndSaveUsingBlock:(void (^)(NSManagedObjectContext *context))aBlock completion:(void (^)(void))completion -{ - NSManagedObjectContext *context = [self newDerivedContext]; - [context performBlock:^{ - aBlock(context); - - [self.contextFactory saveContext:context andWait:NO withCompletionBlock:completion]; - }]; -} - -#pragma mark - Setup - -- (NSPersistentContainer *)persistentContainer -{ - if (_persistentContainer) { - return _persistentContainer; - } - - SentryStartupEvent *startupEvent = [SentryStartupEvent new]; - - [self migrateDataModelsIfNecessary:startupEvent]; - - NSURL *storeURL = self.storeURL; - - // Initialize the container - NSPersistentContainer *persistentContainer = [[NSPersistentContainer alloc] initWithName:@"WordPress" managedObjectModel:self.managedObjectModel]; - _persistentContainer = persistentContainer; - persistentContainer.persistentStoreDescriptions = @[self.storeDescription]; - [persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *description, NSError *error) { - if (error != nil) { - DDLogError(@"Error opening the database. %@\nDeleting the file and trying again", error); - - SentryStartupEventAddError(startupEvent, error); - error = nil; - - // make a backup of the old database - [CoreDataIterativeMigrator backupDatabaseAt:storeURL error:&error]; - if (error != nil) { - SentryStartupEventAddError(startupEvent, error); - error = nil; - } - - [persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *description, NSError *error) { - SentryStartupEventAddError(startupEvent, error); - [startupEvent sendWithTitle:@"Can't initialize Core Data stack"]; - - @throw [NSException exceptionWithName:@"Can't initialize Core Data stack" - reason:[error localizedDescription] - userInfo:[error userInfo]]; - }]; - - } - }]; - return persistentContainer; -} - -- (NSPersistentStoreDescription *)storeDescription -{ - if(_storeDescription) { - return _storeDescription; - } - - NSPersistentStoreDescription *storeDescription = [[NSPersistentStoreDescription alloc] initWithURL:self.storeURL]; - storeDescription.shouldInferMappingModelAutomatically = true; - storeDescription.shouldMigrateStoreAutomatically = true; - return storeDescription; -} - -- (NSManagedObjectModel *)managedObjectModel -{ - if (_managedObjectModel) { - return _managedObjectModel; - } - NSString *modelPath = [self modelPath]; - NSURL *modelURL = [NSURL fileURLWithPath:modelPath]; - if ([self.modelName isEqualToString:ContextManagerModelNameCurrent]) { - // Use the current version defined in data model. - _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; - } else { - // Find the specific version defined in data model. - NSURL *versionedModelURL = [[modelURL URLByAppendingPathComponent:self.modelName] URLByAppendingPathExtension:@"mom"]; - NSAssert([NSFileManager.defaultManager fileExistsAtPath:versionedModelURL.path], - @"Can't find model '%@' at %@", self.modelName, modelPath); - _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:versionedModelURL]; - } - return _managedObjectModel; -} - -#pragma mark - Private Helpers - -- (void)migrateDataModelsIfNecessary:(SentryStartupEvent *)sentryEvent -{ - if (![[NSFileManager defaultManager] fileExistsAtPath:[[self storeURL] path]]) { - DDLogInfo(@"No store exists at URL %@. Skipping migration.", [self storeURL]); - return; - } - - NSDictionary *metadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType - URL:[self storeURL] - options:nil - error:nil]; - BOOL migrationNeeded = ![self.managedObjectModel isConfiguration:nil compatibleWithStoreMetadata:metadata]; - - if (migrationNeeded) { - DDLogWarn(@"Migration required for persistent store."); - NSError *error = nil; - NSArray *sortedModelNames = [self sortedModelNames]; - - [CoreDataIterativeMigrator iterativeMigrateWithSourceStore:[self storeURL] - storeType:NSSQLiteStoreType - to:self.managedObjectModel - using:sortedModelNames - error:&error]; - - if (error != nil) { - DDLogError(@"Unable to migrate store: %@", error); - - SentryStartupEventAddError(sentryEvent, error); - } - } -} - -- (NSArray *)sortedModelNames -{ - NSString *modelPath = [self modelPath]; - NSString *versionPath = [modelPath stringByAppendingPathComponent:@"VersionInfo.plist"]; - NSDictionary *versionInfo = [NSDictionary dictionaryWithContentsOfFile:versionPath]; - NSArray *modelNames = [[versionInfo[@"NSManagedObjectModel_VersionHashes"] allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { - return [obj1 compare:obj2 options:NSNumericSearch]; - }]; - - return modelNames; -} - -- (NSString *)modelPath -{ - return [[NSBundle mainBundle] pathForResource:@"WordPress" ofType:@"momd"]; -} - -- (NSURL *)localDatabasePath -{ - NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, - NSUserDomainMask, - YES) lastObject]; - return [NSURL fileURLWithPath:[documentsDirectory stringByAppendingPathComponent:@"WordPress.sqlite"]]; -} - -@end diff --git a/WordPress/Classes/Utility/ContextManager.swift b/WordPress/Classes/Utility/ContextManager.swift new file mode 100644 index 000000000000..01ec0ce583e6 --- /dev/null +++ b/WordPress/Classes/Utility/ContextManager.swift @@ -0,0 +1,237 @@ +import Foundation +import CoreData + +/// A constant representing the current version of the data model. +/// +/// - SeeAlso: ContextManager.init(modelName:store:) +let ContextManagerModelNameCurrent = "$CURRENT" + +@objc +public class ContextManager: NSObject, CoreDataStack { + static var inMemoryStoreURL: URL { + URL(fileURLWithPath: "/dev/null") + } + + private let modelName: String + private let storeURL: URL + private let persistentContainer: NSPersistentContainer + + @objc + public var mainContext: NSManagedObjectContext { + persistentContainer.viewContext + } + + convenience override init() { + self.init(modelName: ContextManagerModelNameCurrent, store: Self.localDatabasePath) + } + + /// Create a ContextManager instance with given model name and database location. + /// + /// Note: This initialiser is only used for testing purpose at the moment. + /// + /// - Parameters: + /// - modelName: Model name in Core Data data model file. + /// Use ContextManagerModelNameCurrent for current version, or + /// "WordPress " for specific version. + /// - store: Database location. Use `ContextManager.inMemoryStoreURL` to create an in-memory database. + init(modelName: String, store storeURL: URL) { + assert(modelName == ContextManagerModelNameCurrent || modelName.hasPrefix("WordPress ")) + assert(storeURL.isFileURL) + + self.modelName = modelName + self.storeURL = storeURL + self.persistentContainer = Self.createPersistentContainer(storeURL: storeURL, modelName: modelName) + + super.init() + + mainContext.automaticallyMergesChangesFromParent = true + mainContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy + NullBlogPropertySanitizer(context: mainContext).sanitize() + } + + public func newDerivedContext() -> NSManagedObjectContext { + let context = persistentContainer.newBackgroundContext() + context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy + return context + } + + @objc(performAndSaveUsingBlock:) + public func performAndSave(_ block: @escaping (NSManagedObjectContext) -> Void) { + let context = newDerivedContext() + context.performAndWait { + block(context) + + self.save(context, andWait: true, withCompletionBlock: nil) + } + } + + @objc(performAndSaveUsingBlock:completion:) + public func performAndSave(_ block: @escaping (NSManagedObjectContext) -> Void, completion: @escaping () -> Void) { + let context = newDerivedContext() + context.perform { + block(context) + + self.save(context, andWait: false, withCompletionBlock: completion) + } + } + + @objc + public func saveContextAndWait(_ context: NSManagedObjectContext) { + save(context, andWait: true, withCompletionBlock: nil) + } + + @objc(saveContext:) + public func save(_ context: NSManagedObjectContext) { + save(context, andWait: false, withCompletionBlock: nil) + } + + @objc(saveContext:withCompletionBlock:) + public func save(_ context: NSManagedObjectContext, withCompletionBlock completion: @escaping () -> Void) { + save(context, andWait: false, withCompletionBlock: completion) + } +} + +// MARK: - Private methods + +private extension ContextManager { + static var localDatabasePath: URL { + guard let url = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else { + fatalError("Failed to find the document directory") + } + + return url.appendingPathComponent("WordPress.sqlite") + } + + func save(_ context: NSManagedObjectContext, andWait wait: Bool, withCompletionBlock completionBlock: (() -> Void)?) { + let block: () -> Void = { + self.internalSave(context) + DispatchQueue.main.async { + completionBlock?() + } + } + + // Ensure that the `context`'s concurrency type is not `confinementConcurrencyType`, since it will crash if `perform` or `performAndWait` is called. + guard context.concurrencyType == .mainQueueConcurrencyType || context.concurrencyType == .privateQueueConcurrencyType else { + block() + return + } + + if wait { + context.performAndWait(block) + } else { + context.perform(block) + } + } +} + +// MARK: - Initialise Core Data stack + +private extension ContextManager { + static func createPersistentContainer(storeURL: URL, modelName: String) -> NSPersistentContainer { + guard var modelFileURL = Bundle.main.url(forResource: "WordPress", withExtension: "momd") else { + fatalError("Can't find WordPress.momd") + } + + if modelName != ContextManagerModelNameCurrent { + modelFileURL = modelFileURL.appendingPathComponent(modelName).appendingPathExtension("mom") + } + + guard let objectModel = NSManagedObjectModel(contentsOf: modelFileURL) else { + fatalError("Can't create object model named \(modelName) at \(modelFileURL)") + } + + let startupEvent = SentryStartupEvent() + + do { + try migrateDataModelsIfNecessary(storeURL: storeURL, objectModel: objectModel) + } catch { + DDLogError("Unable to migrate store: \(error)") + startupEvent.add(error: error as NSError) + } + + let storeDescription = NSPersistentStoreDescription(url: storeURL) + storeDescription.shouldInferMappingModelAutomatically = true + storeDescription.shouldMigrateStoreAutomatically = true + let persistentContainer = NSPersistentContainer(name: "WordPress", managedObjectModel: objectModel) + persistentContainer.persistentStoreDescriptions = [storeDescription] + persistentContainer.loadPersistentStores { _, error in + guard let error else { + return + } + + DDLogError("Error opening the database. \(error)\nDeleting the file and trying again") + startupEvent.add(error: error) + + // make a backup of the old database + do { + try CoreDataIterativeMigrator.backupDatabase(at: storeURL) + } catch { + startupEvent.add(error: error) + } + + startupEvent.send(title: "Can't initialize Core Data stack") + objc_exception_throw( + NSException( + name: NSExceptionName(rawValue: "Can't initialize Core Data stack"), + reason: error.localizedDescription, + userInfo: (error as NSError).userInfo + ) + ) + } + + return persistentContainer + } + + static func migrateDataModelsIfNecessary(storeURL: URL, objectModel: NSManagedObjectModel) throws { + guard FileManager.default.fileExists(atPath: storeURL.path) else { + DDLogInfo("No store exists at \(storeURL). Skipping migration.") + return + } + + guard let metadata = try? NSPersistentStoreCoordinator.metadataForPersistentStore(ofType: NSSQLiteStoreType, at: storeURL), + objectModel.isConfiguration(withName: nil, compatibleWithStoreMetadata: metadata) + else { + return + } + + DDLogWarn("Migration required for persistent store.") + + guard let modelFileURL = Bundle.main.url(forResource: "WordPress", withExtension: "momd") else { + fatalError("Can't find WordPress.momd") + } + + guard let versionInfo = NSDictionary(contentsOf: modelFileURL.appendingPathComponent("VersionInfo.plist")) else { + fatalError("Can't get the object model's version info") + } + + guard let modelNames = (versionInfo["NSManagedObjectModel_VersionHashes"] as? [String: AnyObject])?.keys else { + fatalError("Can't parse the model versions") + } + + let sortedModelNames = modelNames.sorted { $0.compare($1, options: .numeric) == .orderedAscending } + try CoreDataIterativeMigrator.iterativeMigrate( + sourceStore: storeURL, + storeType: NSSQLiteStoreType, + to: objectModel, + using: sortedModelNames + ) + } +} + +extension ContextManager { + private static let internalSharedInstance = ContextManager() + /// Tests purpose only + static var overrideInstance: CoreDataStack? + + @objc class func sharedInstance() -> CoreDataStack { + if let overrideInstance = overrideInstance { + return overrideInstance + } + + return ContextManager.internalSharedInstance + } + + static var shared: CoreDataStack { + return sharedInstance() + } +} diff --git a/WordPress/Classes/Utility/CoreDataHelper.swift b/WordPress/Classes/Utility/CoreDataHelper.swift index 57e2541bf97a..93af0aeeaca4 100644 --- a/WordPress/Classes/Utility/CoreDataHelper.swift +++ b/WordPress/Classes/Utility/CoreDataHelper.swift @@ -175,25 +175,6 @@ extension NSPersistentStoreCoordinator { // MARK: - ContextManager Helpers extension ContextManager { - static var overrideInstance: CoreDataStack? - - @objc class func sharedInstance() -> CoreDataStack { - if let overrideInstance = overrideInstance { - return overrideInstance - } - - return ContextManager.internalSharedInstance() - } - - static var shared: CoreDataStack { - return sharedInstance() - } - - /// Tests purpose only - @objc public class func overrideSharedInstance(_ instance: CoreDataStack?) { - ContextManager.overrideInstance = instance - } - enum ContextManagerError: Error { case missingCoordinatorOrStore case missingDatabase diff --git a/WordPress/Classes/Utility/CoreDataStack.h b/WordPress/Classes/Utility/CoreDataStack.h new file mode 100644 index 000000000000..a5520f6693bf --- /dev/null +++ b/WordPress/Classes/Utility/CoreDataStack.h @@ -0,0 +1,16 @@ +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol CoreDataStack +@property (nonatomic, readonly, strong) NSManagedObjectContext *mainContext; +- (NSManagedObjectContext *const)newDerivedContext DEPRECATED_MSG_ATTRIBUTE("Use `performAndSave` instead"); +- (void)saveContextAndWait:(NSManagedObjectContext *)context; +- (void)saveContext:(NSManagedObjectContext *)context; +- (void)saveContext:(NSManagedObjectContext *)context withCompletionBlock:(void (^)(void))completionBlock; +- (void)performAndSaveUsingBlock:(void (^)(NSManagedObjectContext *context))aBlock; +- (void)performAndSaveUsingBlock:(void (^)(NSManagedObjectContext *context))aBlock completion:(void (^)(void))completion; +@end + +NS_ASSUME_NONNULL_END diff --git a/WordPress/Classes/Utility/LegacyContextFactory.h b/WordPress/Classes/Utility/LegacyContextFactory.h deleted file mode 100644 index a9a95f475fe2..000000000000 --- a/WordPress/Classes/Utility/LegacyContextFactory.h +++ /dev/null @@ -1,10 +0,0 @@ -#import -#import "ManagedObjectContextFactory.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface LegacyContextFactory : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/WordPress/Classes/Utility/LegacyContextFactory.m b/WordPress/Classes/Utility/LegacyContextFactory.m deleted file mode 100644 index 294ea08aa516..000000000000 --- a/WordPress/Classes/Utility/LegacyContextFactory.m +++ /dev/null @@ -1,124 +0,0 @@ -#import "LegacyContextFactory.h" -#import "WordPress-Swift.h" - -@interface LegacyContextFactory() - -@property (nonatomic, strong) NSPersistentContainer *container; -@property (nonatomic, strong) NSManagedObjectContext *writerContext; -@property (nonatomic, strong) NSManagedObjectContext *mainContext; - -@end - -@implementation LegacyContextFactory - -- (instancetype)initWithPersistentContainer:(NSPersistentContainer *)container -{ - self = [super init]; - if (self) { - self.container = container; - [self createWriterContext]; - [self createMainContext]; - [self startListeningToMainContextNotifications]; - } - return self; -} - -- (NSManagedObjectContext *const)newDerivedContext -{ - return [self newChildContextWithConcurrencyType:NSPrivateQueueConcurrencyType]; -} - -- (void)createWriterContext -{ - NSAssert(self.writerContext == nil, @"%s should only be called once", __PRETTY_FUNCTION__); - - NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; - context.persistentStoreCoordinator = self.container.persistentStoreCoordinator; - - if ([Feature enabled:FeatureFlagContentMigration]) { - context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy; - } - - self.writerContext = context; -} - -- (void)createMainContext -{ - NSAssert(self.mainContext == nil, @"%s should only be called once", __PRETTY_FUNCTION__); - - NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; - context.parentContext = self.writerContext; - self.mainContext = context; -} - -- (NSManagedObjectContext *const)newChildContextWithConcurrencyType:(NSManagedObjectContextConcurrencyType)concurrencyType -{ - NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] - initWithConcurrencyType:concurrencyType]; - childContext.parentContext = self.mainContext; - childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; - - return childContext; -} - -- (void)saveContext:(NSManagedObjectContext *)context andWait:(BOOL)wait withCompletionBlock:(void (^)(void))completionBlock -{ - // Save derived contexts a little differently - if (context.parentContext == self.mainContext) { - [self saveDerivedContext:context andWait:wait withCompletionBlock:completionBlock]; - return; - } - - if (wait) { - [context performBlockAndWait:^{ - [self internalSaveContext:context withCompletionBlock:completionBlock]; - }]; - } else { - [context performBlock:^{ - [self internalSaveContext:context withCompletionBlock:completionBlock]; - }]; - } -} - -- (void)saveDerivedContext:(NSManagedObjectContext *)context andWait:(BOOL)wait withCompletionBlock:(void (^)(void))completionBlock -{ - if (wait) { - [context performBlockAndWait:^{ - [self internalSaveContext:context]; - [self saveContext:self.mainContext andWait:wait withCompletionBlock:completionBlock]; - }]; - } else { - [context performBlock:^{ - [self internalSaveContext:context]; - [self saveContext:self.mainContext andWait:wait withCompletionBlock:completionBlock]; - }]; - } -} - -- (void)internalSaveContext:(NSManagedObjectContext *)context withCompletionBlock:(void (^)(void))completionBlock -{ - [self internalSaveContext:context]; - - if (completionBlock) { - dispatch_async(dispatch_get_main_queue(), completionBlock); - } -} - -#pragma mark - Notification Helpers - -- (void)startListeningToMainContextNotifications -{ - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc addObserver:self selector:@selector(mainContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.mainContext]; -} - -- (void)mainContextDidSave:(NSNotification *)notification -{ - // Defer I/O to a BG Writer Context. Simperium 4ever! - // - [self.writerContext performBlock:^{ - [self internalSaveContext:self.writerContext]; - }]; -} - -@end diff --git a/WordPress/Classes/Utility/ManagedObjectContextFactory.h b/WordPress/Classes/Utility/ManagedObjectContextFactory.h deleted file mode 100644 index ab273de98f5f..000000000000 --- a/WordPress/Classes/Utility/ManagedObjectContextFactory.h +++ /dev/null @@ -1,18 +0,0 @@ -#import - -NS_ASSUME_NONNULL_BEGIN - -/// A type that's used to handle how `NSManagedObjectContext` objects are created and how the data in them are saved. -@protocol ManagedObjectContextFactory - -- (instancetype)initWithPersistentContainer:(NSPersistentContainer *)container; - -@property (nonatomic, readonly, strong) NSManagedObjectContext *mainContext; - -- (NSManagedObjectContext *const)newDerivedContext; - -- (void)saveContext:(NSManagedObjectContext *)context andWait:(BOOL)wait withCompletionBlock:(void (^_Nullable)(void))completionBlock; - -@end - -NS_ASSUME_NONNULL_END diff --git a/WordPress/Classes/Utility/WPAuthTokenIssueSolver.m b/WordPress/Classes/Utility/WPAuthTokenIssueSolver.m index e8e343412900..01420e8e6ac4 100644 --- a/WordPress/Classes/Utility/WPAuthTokenIssueSolver.m +++ b/WordPress/Classes/Utility/WPAuthTokenIssueSolver.m @@ -1,7 +1,7 @@ #import "WPAuthTokenIssueSolver.h" #import "AccountService.h" #import "BlogService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WPAccount.h" #import "WordPress-Swift.h" diff --git a/WordPress/Classes/Utility/WPTableImageSource.m b/WordPress/Classes/Utility/WPTableImageSource.m index 9b80deee604a..43b7b2258907 100644 --- a/WordPress/Classes/Utility/WPTableImageSource.m +++ b/WordPress/Classes/Utility/WPTableImageSource.m @@ -1,6 +1,6 @@ #import "WPTableImageSource.h" #import "AccountService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WPAccount.h" #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Details/BlogDetailsViewController.m b/WordPress/Classes/ViewRelated/Blog/Blog Details/BlogDetailsViewController.m index 06683dab4bc9..50bcab8ddd8f 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Details/BlogDetailsViewController.m +++ b/WordPress/Classes/ViewRelated/Blog/Blog Details/BlogDetailsViewController.m @@ -3,7 +3,7 @@ #import "AccountService.h" #import "BlogService.h" #import "CommentsViewController.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "ReachabilityUtils.h" #import "SiteSettingsViewController.h" #import "SharingViewController.h" diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Selector/BlogSelectorViewController.m b/WordPress/Classes/ViewRelated/Blog/Blog Selector/BlogSelectorViewController.m index b02f0ab0880c..bbe5aef8e8c8 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Selector/BlogSelectorViewController.m +++ b/WordPress/Classes/ViewRelated/Blog/Blog Selector/BlogSelectorViewController.m @@ -1,7 +1,7 @@ #import "BlogSelectorViewController.h" #import "BlogDetailsViewController.h" #import "WPBlogTableViewCell.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "Blog.h" #import "WPAccount.h" #import "AccountService.h" diff --git a/WordPress/Classes/ViewRelated/Blog/Site Settings/Related Posts/RelatedPostsSettingsViewController.m b/WordPress/Classes/ViewRelated/Blog/Site Settings/Related Posts/RelatedPostsSettingsViewController.m index a4b82090a9d3..6217a31fb376 100644 --- a/WordPress/Classes/ViewRelated/Blog/Site Settings/Related Posts/RelatedPostsSettingsViewController.m +++ b/WordPress/Classes/ViewRelated/Blog/Site Settings/Related Posts/RelatedPostsSettingsViewController.m @@ -2,7 +2,7 @@ #import "Blog.h" #import "BlogService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "SettingTableViewCell.h" #import "SVProgressHud+Dismiss.h" #import "RelatedPostsPreviewTableViewCell.h" diff --git a/WordPress/Classes/ViewRelated/Blog/Site Settings/SiteSettingsViewController.m b/WordPress/Classes/ViewRelated/Blog/Site Settings/SiteSettingsViewController.m index 324ef99f5982..e96830b0ef25 100644 --- a/WordPress/Classes/ViewRelated/Blog/Site Settings/SiteSettingsViewController.m +++ b/WordPress/Classes/ViewRelated/Blog/Site Settings/SiteSettingsViewController.m @@ -3,7 +3,7 @@ #import "Blog.h" #import "BlogService.h" #import "BlogSiteVisibilityHelper.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "NSURL+IDN.h" #import "PostCategory.h" #import "PostCategoryService.h" diff --git a/WordPress/Classes/ViewRelated/Comments/EditCommentViewController.m b/WordPress/Classes/ViewRelated/Comments/EditCommentViewController.m index 8588baf0f748..726021687161 100644 --- a/WordPress/Classes/ViewRelated/Comments/EditCommentViewController.m +++ b/WordPress/Classes/ViewRelated/Comments/EditCommentViewController.m @@ -1,6 +1,6 @@ #import "EditCommentViewController.h" #import "CommentService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/Media/MediaLibraryPickerDataSource.m b/WordPress/Classes/ViewRelated/Media/MediaLibraryPickerDataSource.m index 87bd87adb740..60d3df81664e 100644 --- a/WordPress/Classes/ViewRelated/Media/MediaLibraryPickerDataSource.m +++ b/WordPress/Classes/ViewRelated/Media/MediaLibraryPickerDataSource.m @@ -2,7 +2,7 @@ #import "Media.h" #import "MediaService.h" #import "Blog.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WordPress-Swift.h" @interface MediaLibraryPickerDataSource() diff --git a/WordPress/Classes/ViewRelated/Menus/MenuItemEditingViewController.m b/WordPress/Classes/ViewRelated/Menus/MenuItemEditingViewController.m index e08815beff5a..ed85f186cad4 100644 --- a/WordPress/Classes/ViewRelated/Menus/MenuItemEditingViewController.m +++ b/WordPress/Classes/ViewRelated/Menus/MenuItemEditingViewController.m @@ -6,7 +6,7 @@ #import "MenuItemEditingFooterView.h" #import "MenuItemSourceViewController.h" #import "MenuItemTypeViewController.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import #import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/Menus/MenuItemsViewController.m b/WordPress/Classes/ViewRelated/Menus/MenuItemsViewController.m index 88754e86ec56..6b0377a3f9fb 100644 --- a/WordPress/Classes/ViewRelated/Menus/MenuItemsViewController.m +++ b/WordPress/Classes/ViewRelated/Menus/MenuItemsViewController.m @@ -5,7 +5,7 @@ #import "MenuItemView.h" #import "MenuItemInsertionView.h" #import "MenuItemsVisualOrderingView.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "Menu+ViewDesign.h" #import "WPGUIConstants.h" #import diff --git a/WordPress/Classes/ViewRelated/Menus/MenusViewController.m b/WordPress/Classes/ViewRelated/Menus/MenusViewController.m index 7d32f5cf5c4f..088e710624dd 100644 --- a/WordPress/Classes/ViewRelated/Menus/MenusViewController.m +++ b/WordPress/Classes/ViewRelated/Menus/MenusViewController.m @@ -9,7 +9,7 @@ #import "MenuItemsViewController.h" #import "MenuItemEditingViewController.h" #import "Menu+ViewDesign.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "WPAppAnalytics.h" #import "WordPress-Swift.h" #import diff --git a/WordPress/Classes/ViewRelated/Post/Categories/WPAddPostCategoryViewController.m b/WordPress/Classes/ViewRelated/Post/Categories/WPAddPostCategoryViewController.m index b8a15c1d1f50..ae825d151505 100644 --- a/WordPress/Classes/ViewRelated/Post/Categories/WPAddPostCategoryViewController.m +++ b/WordPress/Classes/ViewRelated/Post/Categories/WPAddPostCategoryViewController.m @@ -4,7 +4,7 @@ #import "Constants.h" #import "SiteSettingsViewController.h" #import "PostCategoryService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "BlogService.h" #import "WordPress-Swift.h" #import diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m index 0d1e394e9f26..41e7019fa585 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m +++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m @@ -7,7 +7,7 @@ #import "SharingDetailViewController.h" #import "WPTableViewActivityCell.h" #import "WPTableImageSource.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "MediaService.h" #import "WPProgressTableViewCell.h" #import "WPAndDeviceMediaLibraryDataSource.h" diff --git a/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m b/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m index dc1ee596bf05..b2e7f8829265 100644 --- a/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m +++ b/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m @@ -1,7 +1,7 @@ #import "ReaderCommentsViewController.h" #import "CommentService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "ReaderPost.h" #import "ReaderPostService.h" #import "UIView+Subviews.h" diff --git a/WordPress/Classes/ViewRelated/Stats/StatsViewController.m b/WordPress/Classes/ViewRelated/Stats/StatsViewController.m index 46626fb4cff4..d2be0d074633 100644 --- a/WordPress/Classes/ViewRelated/Stats/StatsViewController.m +++ b/WordPress/Classes/ViewRelated/Stats/StatsViewController.m @@ -4,7 +4,7 @@ #import "StatsViewController.h" #import "Blog.h" #import "WPAccount.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "BlogService.h" #import "TodayExtensionService.h" #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/System/WPTabBarController.m b/WordPress/Classes/ViewRelated/System/WPTabBarController.m index 99b19a4dfb1c..61739f665c1b 100644 --- a/WordPress/Classes/ViewRelated/System/WPTabBarController.m +++ b/WordPress/Classes/ViewRelated/System/WPTabBarController.m @@ -2,7 +2,7 @@ #import #import "AccountService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "BlogService.h" #import "Blog.h" diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 66057660dcb4..55acee0c99a4 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -1105,14 +1105,12 @@ 4A2172FF28F688890006F4F1 /* Blog+Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2172FD28F688890006F4F1 /* Blog+Media.swift */; }; 4A266B8F282B05210089CF3D /* JSONObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A266B8E282B05210089CF3D /* JSONObjectTests.swift */; }; 4A266B91282B13A70089CF3D /* CoreDataTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A266B90282B13A70089CF3D /* CoreDataTestCase.swift */; }; - 4A50667C28B3216500DD09F4 /* LegacyContextFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A50667B28B3216500DD09F4 /* LegacyContextFactory.m */; }; - 4A50667D28B3216500DD09F4 /* LegacyContextFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A50667B28B3216500DD09F4 /* LegacyContextFactory.m */; }; - 4A50668028B364CA00DD09F4 /* ContainerContextFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A50667F28B364CA00DD09F4 /* ContainerContextFactory.swift */; }; - 4A50668128B364CA00DD09F4 /* ContainerContextFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A50667F28B364CA00DD09F4 /* ContainerContextFactory.swift */; }; 4A82C43128D321A300486CFF /* Blog+Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82C43028D321A300486CFF /* Blog+Post.swift */; }; 4A82C43228D321A300486CFF /* Blog+Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82C43028D321A300486CFF /* Blog+Post.swift */; }; 4A878550290F2C7D0083AB78 /* Media+Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A87854F290F2C7D0083AB78 /* Media+Sync.swift */; }; 4A878551290F2C7D0083AB78 /* Media+Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A87854F290F2C7D0083AB78 /* Media+Sync.swift */; }; + 4A9B81E32921AE03007A05D1 /* ContextManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A9B81E22921AE02007A05D1 /* ContextManager.swift */; }; + 4A9B81E42921AE03007A05D1 /* ContextManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A9B81E22921AE02007A05D1 /* ContextManager.swift */; }; 4AD5656C28E3D0670054C676 /* ReaderPost+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD5656B28E3D0670054C676 /* ReaderPost+Helper.swift */; }; 4AD5656D28E3D0670054C676 /* ReaderPost+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD5656B28E3D0670054C676 /* ReaderPost+Helper.swift */; }; 4AD5656F28E413160054C676 /* Blog+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AD5656E28E413160054C676 /* Blog+History.swift */; }; @@ -2627,7 +2625,6 @@ C3FF78E828354A91008FA600 /* SiteDesignSectionLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3FF78E728354A91008FA600 /* SiteDesignSectionLoader.swift */; }; C3FF78E928354A91008FA600 /* SiteDesignSectionLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3FF78E728354A91008FA600 /* SiteDesignSectionLoader.swift */; }; C533CF350E6D3ADA000C3DE8 /* CommentsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C533CF340E6D3ADA000C3DE8 /* CommentsViewController.m */; }; - C545E0A21811B9880020844C /* ContextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C545E0A11811B9880020844C /* ContextManager.m */; }; C56636E91868D0CE00226AAB /* StatsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C56636E71868D0CE00226AAB /* StatsViewController.m */; }; C649C66318E8B5EF92B8F196 /* Pods_JetpackIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D42A30853435E728881904E8 /* Pods_JetpackIntents.framework */; }; C700F9D2257FD63A0090938E /* JetpackScanViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C700F9D0257FD63A0090938E /* JetpackScanViewController.xib */; }; @@ -3810,7 +3807,6 @@ FABB20DC2602FC2C00C8785C /* WPAppAnalytics+Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0828D7F91E6E09AE00C7C7D4 /* WPAppAnalytics+Media.swift */; }; FABB20DD2602FC2C00C8785C /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 591AA4FF1CEF9BF20074934F /* Post.swift */; }; FABB20DE2602FC2C00C8785C /* GutenbergWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0FF01D242BC572008DA898 /* GutenbergWebViewController.swift */; }; - FABB20DF2602FC2C00C8785C /* ContextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C545E0A11811B9880020844C /* ContextManager.m */; }; FABB20E02602FC2C00C8785C /* CookieJar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E161B7EB1F839345000FDF0B /* CookieJar.swift */; }; FABB20E12602FC2C00C8785C /* WebAddressStep.swift in Sources */ = {isa = PBXBuildFile; fileRef = D853723921952DAF0076F461 /* WebAddressStep.swift */; }; FABB20E22602FC2C00C8785C /* NotificationSyncMediator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F67AC61DB7D81300482C62 /* NotificationSyncMediator.swift */; }; @@ -5209,7 +5205,7 @@ FE25C235271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE25C234271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift */; }; FE25C236271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE25C234271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift */; }; FE2E3729281C839C00A1E82A /* BloggingPromptsServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2E3728281C839C00A1E82A /* BloggingPromptsServiceTests.swift */; }; - FE320CC5294705990046899B /* ContainerContextFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE320CC4294705990046899B /* ContainerContextFactoryTests.swift */; }; + FE320CC5294705990046899B /* ReaderPostBackupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE320CC4294705990046899B /* ReaderPostBackupTests.swift */; }; FE32E7F12844971000744D80 /* ReminderScheduleCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE32E7F02844971000744D80 /* ReminderScheduleCoordinatorTests.swift */; }; FE32EFFF275914390040BE67 /* MenuSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE32EFFE275914390040BE67 /* MenuSheetViewController.swift */; }; FE32F000275914390040BE67 /* MenuSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE32EFFE275914390040BE67 /* MenuSheetViewController.swift */; }; @@ -6428,12 +6424,9 @@ 4A2172FD28F688890006F4F1 /* Blog+Media.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Blog+Media.swift"; sourceTree = ""; }; 4A266B8E282B05210089CF3D /* JSONObjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONObjectTests.swift; sourceTree = ""; }; 4A266B90282B13A70089CF3D /* CoreDataTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataTestCase.swift; sourceTree = ""; }; - 4A50667A28B3216500DD09F4 /* LegacyContextFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LegacyContextFactory.h; sourceTree = ""; }; - 4A50667B28B3216500DD09F4 /* LegacyContextFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LegacyContextFactory.m; sourceTree = ""; }; - 4A50667E28B3218800DD09F4 /* ManagedObjectContextFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ManagedObjectContextFactory.h; sourceTree = ""; }; - 4A50667F28B364CA00DD09F4 /* ContainerContextFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerContextFactory.swift; sourceTree = ""; }; 4A82C43028D321A300486CFF /* Blog+Post.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Blog+Post.swift"; sourceTree = ""; }; 4A87854F290F2C7D0083AB78 /* Media+Sync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Media+Sync.swift"; sourceTree = ""; }; + 4A9B81E22921AE02007A05D1 /* ContextManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextManager.swift; sourceTree = ""; }; 4AD5656B28E3D0670054C676 /* ReaderPost+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReaderPost+Helper.swift"; sourceTree = ""; }; 4AD5656E28E413160054C676 /* Blog+History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Blog+History.swift"; sourceTree = ""; }; 4AD5657128E543A30054C676 /* BlogQueryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogQueryTests.swift; sourceTree = ""; }; @@ -7751,8 +7744,7 @@ C52812131832E071008931FD /* WordPress 13.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 13.xcdatamodel"; sourceTree = ""; }; C533CF330E6D3ADA000C3DE8 /* CommentsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommentsViewController.h; sourceTree = ""; }; C533CF340E6D3ADA000C3DE8 /* CommentsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommentsViewController.m; sourceTree = ""; }; - C545E0A01811B9880020844C /* ContextManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContextManager.h; sourceTree = ""; }; - C545E0A11811B9880020844C /* ContextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContextManager.m; sourceTree = ""; }; + C545E0A01811B9880020844C /* CoreDataStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreDataStack.h; sourceTree = ""; }; C56636E61868D0CE00226AAB /* StatsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StatsViewController.h; sourceTree = ""; }; C56636E71868D0CE00226AAB /* StatsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = StatsViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; C5E82422F47D9BF7E682262B /* Pods-JetpackDraftActionExtension.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JetpackDraftActionExtension.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-JetpackDraftActionExtension/Pods-JetpackDraftActionExtension.release-alpha.xcconfig"; sourceTree = ""; }; @@ -8811,7 +8803,7 @@ FE23EB4826E7C91F005A1698 /* richCommentStyle.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = richCommentStyle.css; path = Resources/HTML/richCommentStyle.css; sourceTree = ""; }; FE25C234271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderCommentsNotificationSheetViewController.swift; sourceTree = ""; }; FE2E3728281C839C00A1E82A /* BloggingPromptsServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BloggingPromptsServiceTests.swift; sourceTree = ""; }; - FE320CC4294705990046899B /* ContainerContextFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerContextFactoryTests.swift; sourceTree = ""; }; + FE320CC4294705990046899B /* ReaderPostBackupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderPostBackupTests.swift; sourceTree = ""; }; FE32E7F02844971000744D80 /* ReminderScheduleCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReminderScheduleCoordinatorTests.swift; sourceTree = ""; }; FE32E7F32846A68800744D80 /* WordPress 142.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 142.xcdatamodel"; sourceTree = ""; }; FE32EFFE275914390040BE67 /* MenuSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuSheetViewController.swift; sourceTree = ""; }; @@ -14566,7 +14558,7 @@ 93E9050319E6F242005513C9 /* ContextManagerTests.swift */, B5ECA6CC1DBAAD510062D7E0 /* CoreDataHelperTests.swift */, 931D26FF19EDAE8600114F17 /* CoreDataMigrationTests.m */, - FE320CC4294705990046899B /* ContainerContextFactoryTests.swift */, + FE320CC4294705990046899B /* ReaderPostBackupTests.swift */, ); name = "Core Data"; sourceTree = ""; @@ -14698,12 +14690,8 @@ isa = PBXGroup; children = ( 3FD83CBD246C74B800381999 /* Migrator */, - C545E0A01811B9880020844C /* ContextManager.h */, - C545E0A11811B9880020844C /* ContextManager.m */, - 4A50667E28B3218800DD09F4 /* ManagedObjectContextFactory.h */, - 4A50667A28B3216500DD09F4 /* LegacyContextFactory.h */, - 4A50667B28B3216500DD09F4 /* LegacyContextFactory.m */, - 4A50667F28B364CA00DD09F4 /* ContainerContextFactory.swift */, + C545E0A01811B9880020844C /* CoreDataStack.h */, + 4A9B81E22921AE02007A05D1 /* ContextManager.swift */, E1E5EE36231E47A80018E9E3 /* ContextManager+ErrorHandling.swift */, B5ECA6C91DBAA0020062D7E0 /* CoreDataHelper.swift */, 24F3789725E6E62100A27BB7 /* NSManagedObject+Lookup.swift */, @@ -19980,7 +19968,6 @@ C7A09A4D28403A34003096ED /* QRLoginURLParser.swift in Sources */, 591AA5011CEF9BF20074934F /* Post.swift in Sources */, 1E0FF01E242BC572008DA898 /* GutenbergWebViewController.swift in Sources */, - C545E0A21811B9880020844C /* ContextManager.m in Sources */, E161B7EC1F839345000FDF0B /* CookieJar.swift in Sources */, C7F7BDD026262A4C00CE547F /* AppDependency.swift in Sources */, C7AFF874283C0ADC000E01DF /* UIApplication+Helpers.swift in Sources */, @@ -20333,7 +20320,6 @@ 8217380B1FE05EE600BEC94C /* BlogSettings+DateAndTimeFormat.swift in Sources */, 57047A4F22A961BC00B461DF /* PostSearchHeader.swift in Sources */, 088B89891DA6F93B000E8DEF /* ReaderPostCardContentLabel.swift in Sources */, - 4A50667C28B3216500DD09F4 /* LegacyContextFactory.m in Sources */, C3C2F84828AC8EBF00937E45 /* JetpackBannerScrollVisibility.swift in Sources */, F181EDE526B2AC7200C61241 /* BackgroundTasksCoordinator.swift in Sources */, 8B93412F257029F60097D0AC /* FilterChipButton.swift in Sources */, @@ -20929,7 +20915,6 @@ 17523381246C4F9200870B4A /* HomepageSettingsViewController.swift in Sources */, E62CE58E26B1D14200C9D147 /* AccountService+Cookies.swift in Sources */, C81CCD7F243BF7A600A83E27 /* TenorMediaGroup.swift in Sources */, - 4A50668028B364CA00DD09F4 /* ContainerContextFactory.swift in Sources */, B0F2EFBF259378E600C7EB6D /* SiteSuggestionService.swift in Sources */, 4388FF0020A4E19C00783948 /* NotificationsViewController+PushPrimer.swift in Sources */, 800035BD291DD0D7007D2D26 /* JetpackFullscreenOverlayGeneralViewModel+Analytics.swift in Sources */, @@ -21228,6 +21213,7 @@ 5DBCD9D518F35D7500B32229 /* ReaderTopicService.m in Sources */, 2F09D134245223D300956257 /* HeaderDetailsContentStyles.swift in Sources */, 403F57BC20E5CA6A004E889A /* RewindStatusRow.swift in Sources */, + 4A9B81E32921AE03007A05D1 /* ContextManager.swift in Sources */, F5D0A64923C8FA1500B20D27 /* LinkBehavior.swift in Sources */, E6DE44671B90D251000FA7EF /* ReaderHelpers.swift in Sources */, C7BB601C2863B3D600748FD9 /* QRLoginCameraPermissionsHandler.swift in Sources */, @@ -22321,7 +22307,7 @@ C738CB1128626606001BE107 /* QRLoginVerifyCoordinatorTests.swift in Sources */, FF0B2567237A023C004E255F /* GutenbergVideoUploadProcessorTests.swift in Sources */, FF1B11E7238FE27A0038B93E /* GutenbergGalleryUploadProcessorTests.swift in Sources */, - FE320CC5294705990046899B /* ContainerContextFactoryTests.swift in Sources */, + FE320CC5294705990046899B /* ReaderPostBackupTests.swift in Sources */, F4D9AF51288AE23500803D40 /* SuggestionTableViewTests.swift in Sources */, 8BC12F72231FEBA1004DDA72 /* PostCoordinatorTests.swift in Sources */, C3C2F84628AC8BC700937E45 /* JetpackBannerScrollVisibilityTests.swift in Sources */, @@ -22634,7 +22620,6 @@ FABB20DD2602FC2C00C8785C /* Post.swift in Sources */, FA8E2FE127C6377000DA0982 /* DashboardQuickStartCardCell.swift in Sources */, FABB20DE2602FC2C00C8785C /* GutenbergWebViewController.swift in Sources */, - FABB20DF2602FC2C00C8785C /* ContextManager.m in Sources */, FABB20E02602FC2C00C8785C /* CookieJar.swift in Sources */, C7F7BDBD26262A1B00CE547F /* AppDependency.swift in Sources */, FAB37D4727ED84BC00CA993C /* DashboardStatsNudgeView.swift in Sources */, @@ -23348,7 +23333,6 @@ F1D8C6EC26BABE3E002E3323 /* WeeklyRoundupDebugScreen.swift in Sources */, FABB23072602FC2C00C8785C /* SiteCreationRotatingMessageView.swift in Sources */, FABB23082602FC2C00C8785C /* SignupUsernameViewController.swift in Sources */, - 4A50667D28B3216500DD09F4 /* LegacyContextFactory.m in Sources */, FABB23092602FC2C00C8785C /* MediaItemViewController.swift in Sources */, FABB230A2602FC2C00C8785C /* PostAnnotation.m in Sources */, FABB230B2602FC2C00C8785C /* GutenbergViewController.swift in Sources */, @@ -23894,6 +23878,7 @@ FABB24BB2602FC2C00C8785C /* ThemeBrowserViewController.swift in Sources */, FA73D7ED27987E4500DF24B3 /* SitePickerViewController+QuickStart.swift in Sources */, FABB24BC2602FC2C00C8785C /* RevisionOperationViewController.swift in Sources */, + 4A9B81E42921AE03007A05D1 /* ContextManager.swift in Sources */, FABB24BD2602FC2C00C8785C /* WPBlogSelectorButton.m in Sources */, FABB24BE2602FC2C00C8785C /* ReaderTabView.swift in Sources */, F49B9A08293A21F4000CEFCE /* MigrationEvent.swift in Sources */, @@ -24206,7 +24191,6 @@ FABB25AE2602FC2C00C8785C /* SearchWrapperView.swift in Sources */, 98830A932747043B0061A87C /* BorderedButtonTableViewCell.swift in Sources */, FABB25AF2602FC2C00C8785C /* UserSettings.swift in Sources */, - 4A50668128B364CA00DD09F4 /* ContainerContextFactory.swift in Sources */, FABB25B02602FC2C00C8785C /* MediaImportService.swift in Sources */, FABB25B12602FC2C00C8785C /* NSAttributedStringKey+Conversion.swift in Sources */, FABB25B22602FC2C00C8785C /* SelectPostViewController.swift in Sources */, diff --git a/WordPress/WordPressTest/Blog+ObjcTests.m b/WordPress/WordPressTest/Blog+ObjcTests.m index 28d49d04102e..950133aa6bc7 100644 --- a/WordPress/WordPressTest/Blog+ObjcTests.m +++ b/WordPress/WordPressTest/Blog+ObjcTests.m @@ -2,13 +2,13 @@ #import "WordPressTest-Swift.h" @interface Blog_ObjcTests : XCTestCase -@property (strong, nonatomic) ContextManager *contextManager; +@property (strong, nonatomic) id contextManager; @end @implementation Blog_ObjcTests - (void)setUp { - self.contextManager = [ContextManager forTesting]; + self.contextManager = [self coreDataStackForTesting]; [super setUp]; } diff --git a/WordPress/WordPressTest/BlogJetpackTest.m b/WordPress/WordPressTest/BlogJetpackTest.m index c440d09d7dd2..f43aaa83e5a3 100644 --- a/WordPress/WordPressTest/BlogJetpackTest.m +++ b/WordPress/WordPressTest/BlogJetpackTest.m @@ -3,7 +3,7 @@ #import "Blog.h" #import "WPAccount.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "AccountService.h" #import "BlogService.h" #import "WordPressTest-Swift.h" @@ -17,14 +17,14 @@ @interface BlogJetpackTest () @property (nonatomic, strong) WPAccount *account; @property (nonatomic, strong) Blog *blog; -@property (nonatomic, strong) ContextManager *testContextManager; +@property (nonatomic, strong) id testContextManager; @end @implementation BlogJetpackTest - (void)setUp { [super setUp]; - self.testContextManager = [ContextManager forTesting]; + self.testContextManager = [self coreDataStackForTesting]; _blog = (Blog *)[NSEntityDescription insertNewObjectForEntityForName:@"Blog" inManagedObjectContext:self.testContextManager.mainContext]; diff --git a/WordPress/WordPressTest/BlogServiceTest.m b/WordPress/WordPressTest/BlogServiceTest.m index d6fc59f4007b..a08a60ed2cd0 100644 --- a/WordPress/WordPressTest/BlogServiceTest.m +++ b/WordPress/WordPressTest/BlogServiceTest.m @@ -2,7 +2,7 @@ #import #import "AccountService.h" #import "BlogService.h" -#import "ContextManager.h" +#import "CoreDataStack.h" #import "Blog.h" #import "WPAccount.h" #import "WordPressTest-Swift.h" @@ -12,7 +12,7 @@ @interface BlogServiceTest : XCTestCase @property (nonatomic, strong) BlogService *blogService; @property (nonatomic, strong) id blogServiceMock; @property (nonatomic, strong) Blog *blog; -@property (nonatomic, strong) ContextManager *coreDataStack; +@property (nonatomic, strong) id coreDataStack; @end @@ -22,7 +22,7 @@ - (void)setUp { [super setUp]; - self.coreDataStack = [ContextManager forTesting]; + self.coreDataStack = [self coreDataStackForTesting]; self.blogService = [[BlogService alloc] initWithManagedObjectContext:[self.coreDataStack mainContext]]; AccountService *service = [[AccountService alloc] initWithManagedObjectContext:self.coreDataStack.mainContext]; diff --git a/WordPress/WordPressTest/BlogSiteVisibilityHelperTest.m b/WordPress/WordPressTest/BlogSiteVisibilityHelperTest.m index 3a25acdc3f3f..f21fb0051ada 100644 --- a/WordPress/WordPressTest/BlogSiteVisibilityHelperTest.m +++ b/WordPress/WordPressTest/BlogSiteVisibilityHelperTest.m @@ -9,7 +9,7 @@ @interface BlogSiteVisibilityHelperTest : XCTestCase @end @interface BlogSiteVisibilityHelperTest () -@property (nonatomic, strong) ContextManager *coreDataStack; +@property (nonatomic, strong) id coreDataStack; @end @implementation BlogSiteVisibilityHelperTest @@ -18,7 +18,7 @@ - (void)setUp { [super setUp]; - self.coreDataStack = [ContextManager forTesting]; + self.coreDataStack = [self coreDataStackForTesting]; } - (void)tearDown diff --git a/WordPress/WordPressTest/BlogTimeZoneTests.m b/WordPress/WordPressTest/BlogTimeZoneTests.m index adb55dd8f962..c4a113898ce7 100644 --- a/WordPress/WordPressTest/BlogTimeZoneTests.m +++ b/WordPress/WordPressTest/BlogTimeZoneTests.m @@ -5,7 +5,7 @@ #import "WordPressTest-Swift.h" @interface BlogTimeZoneTests : XCTestCase -@property (nonatomic, strong) ContextManager *coreDataStack; +@property (nonatomic, strong) id coreDataStack; @property (nonatomic, strong) Blog *blog; @end @@ -15,7 +15,7 @@ - (void)setUp { [super setUp]; - self.coreDataStack = [ContextManager forTesting]; + self.coreDataStack = [self coreDataStackForTesting]; AccountService *service = [[AccountService alloc] initWithManagedObjectContext: self.coreDataStack.mainContext]; WPAccount *account = [service createOrUpdateAccountWithUsername:@"test" authToken:@"token"]; diff --git a/WordPress/WordPressTest/ContainerContextFactoryTests.swift b/WordPress/WordPressTest/ContainerContextFactoryTests.swift deleted file mode 100644 index d7abb99219b1..000000000000 --- a/WordPress/WordPressTest/ContainerContextFactoryTests.swift +++ /dev/null @@ -1,112 +0,0 @@ -import XCTest - -@testable import WordPress - -final class ContainerContextFactoryTests: XCTestCase { - - private var persistentContainer: NSPersistentContainer! - private var factory: ContainerContextFactory! - - override func setUp() { - super.setUp() - - persistentContainer = makeInMemoryContainer() - factory = ContainerContextFactory(persistentContainer: persistentContainer) - } - - override func tearDown() { - persistentContainer = nil - factory = nil - - super.tearDown() - } - - // MARK: - `save` Tests - - func test_save_givenMainQueueConcurrencyType_shouldCallPerform() { - let context = makeMockContext(concurrencyType: .mainQueueConcurrencyType) - - factory.save(context, andWait: false, withCompletionBlock: nil) - - XCTAssertTrue(context.performCalled) - XCTAssertFalse(context.performAndWaitCalled) - } - - func test_save_givenMainQueueConcurrencyType_andWaitIsSetToTrue_shouldCallPerformAndWait() { - let context = makeMockContext(concurrencyType: .mainQueueConcurrencyType) - - factory.save(context, andWait: true, withCompletionBlock: nil) - - XCTAssertFalse(context.performCalled) - XCTAssertTrue(context.performAndWaitCalled) - } - - func test_save_givenPrivateQueueConcurrencyType_shouldCallPerform() { - let context = makeMockContext(concurrencyType: .privateQueueConcurrencyType) - - factory.save(context, andWait: false, withCompletionBlock: nil) - - XCTAssertTrue(context.performCalled) - XCTAssertFalse(context.performAndWaitCalled) - } - - func test_save_givenPrivateQueueConcurrencyType_andWaitIsSetToTrue_shouldCallPerformAndWait() { - let context = makeMockContext(concurrencyType: .privateQueueConcurrencyType) - - factory.save(context, andWait: true, withCompletionBlock: nil) - - XCTAssertFalse(context.performCalled) - XCTAssertTrue(context.performAndWaitCalled) - } - - func test_save_givenDeprecatedConcurrencyType_shouldNotCallPerform() { - /// creates a context with `.confinementConcurrencyType`. The enum is created from its raw value - /// to prevent Xcode from complaining since the enum value is deprecated. - let context = makeMockContext(concurrencyType: NSManagedObjectContextConcurrencyType(rawValue: 0)!) - - factory.save(context, andWait: false, withCompletionBlock: nil) - - XCTAssertFalse(context.performCalled) - XCTAssertFalse(context.performAndWaitCalled) - } -} - -// MARK: - Private Helpers - -private extension ContainerContextFactoryTests { - - class MockManagedObjectContext: NSManagedObjectContext { - - var performCalled = false - var performAndWaitCalled = false - - override func perform(_ block: @escaping () -> Void) { - performCalled = true - } - - override func performAndWait(_ block: () -> Void) { - performAndWaitCalled = true - } - - override func save() throws { - // do nothing. let's make sure nothing gets saved. - } - } - - /// Creates an "in-memory" NSPersistentContainer. - /// This follows the approach used in WWDC'18: https://developer.apple.com/videos/play/wwdc2018/224/ - /// - /// - Returns: An instance of NSPersistentContainer that stores data in memory. - func makeInMemoryContainer() -> NSPersistentContainer { - let container = NSPersistentContainer(name: "WordPress") - let description = NSPersistentStoreDescription(url: .init(fileURLWithPath: "/dev/null")) - container.persistentStoreDescriptions = [description] - - return container - } - - func makeMockContext(concurrencyType: NSManagedObjectContextConcurrencyType) -> MockManagedObjectContext { - return MockManagedObjectContext(concurrencyType: concurrencyType) - } - -} diff --git a/WordPress/WordPressTest/ContextManager+Helpers.swift b/WordPress/WordPressTest/ContextManager+Helpers.swift index f798bfb205b2..bb916aa8a508 100644 --- a/WordPress/WordPressTest/ContextManager+Helpers.swift +++ b/WordPress/WordPressTest/ContextManager+Helpers.swift @@ -7,9 +7,8 @@ extension ContextManager { /// Create an in memory database. /// /// - SeeAlso `ContextManager` - @objc static func forTesting() -> ContextManager { - ContextManager(modelName: ContextManagerModelNameCurrent, store: ContextManager.inMemoryStoreURL, contextFactory: nil) + ContextManager(modelName: ContextManagerModelNameCurrent, store: ContextManager.inMemoryStoreURL) } /// Override `ContextManager.shared` with the mock instance, and un-override it when @@ -24,7 +23,7 @@ extension ContextManager { _ = AutomaticTeardownTestObserver.instance let original = ContextManager.overrideInstance - ContextManager.overrideSharedInstance(self) + ContextManager.overrideInstance = self // This closure is going to be called by the test observer below when // the test case finishes. The reason a combination of storing a closure in @@ -44,7 +43,7 @@ extension ContextManager { } self.mainContext.reset() if ContextManager.overrideInstance === self { - ContextManager.overrideSharedInstance(original) + ContextManager.overrideInstance = original } } } diff --git a/WordPress/WordPressTest/ContextManagerTests.swift b/WordPress/WordPressTest/ContextManagerTests.swift index b7cbebd54045..b0ae176a793a 100644 --- a/WordPress/WordPressTest/ContextManagerTests.swift +++ b/WordPress/WordPressTest/ContextManagerTests.swift @@ -41,7 +41,7 @@ class ContextManagerTests: XCTestCase { } // Migrate to the latest version - let contextManager = ContextManager(modelName: ContextManagerModelNameCurrent, store: storeURL, contextFactory: nil) + let contextManager = ContextManager(modelName: ContextManagerModelNameCurrent, store: storeURL) let object = try contextManager.mainContext.existingObject(with: XCTUnwrap(objectID)) XCTAssertNotNil(object, "Object should exist in new PSC") @@ -79,7 +79,7 @@ class ContextManagerTests: XCTestCase { } // Migrate to the latest - let contextManager = ContextManager(modelName: ContextManagerModelNameCurrent, store: storeURL, contextFactory: nil) + let contextManager = ContextManager(modelName: ContextManagerModelNameCurrent, store: storeURL) let object = try contextManager.mainContext.existingObject(with: XCTUnwrap(objectID)) XCTAssertNotNil(object, "Object should exist in new PSC") XCTAssertNoThrow(object.value(forKey: "author"), "Theme.author should exist in current model version, but we were unable to fetch it") @@ -106,7 +106,7 @@ class ContextManagerTests: XCTestCase { } // Initialize 24 > 25 Migration - let contextManager = ContextManager(modelName: model25Name, store: storeURL, contextFactory: nil) + let contextManager = ContextManager(modelName: model25Name, store: storeURL) let secondContext = contextManager.mainContext // Test the existence of Post object after migration @@ -185,14 +185,14 @@ class ContextManagerTests: XCTestCase { expect(try findFirstUser()?.username) == "First User" } - func testSaveUsingBlock() async { + func testSaveUsingBlock() async throws { let contextManager = ContextManager.forTesting() let numberOfAccounts: () -> Int = { contextManager.mainContext.countObjects(ofType: WPAccount.self) } XCTAssertEqual(numberOfAccounts(), 0) - await contextManager.performAndSave { context in + try await contextManager.performAndSave { context in let account = WPAccount(context: context) account.userID = 1 account.username = "First User" diff --git a/WordPress/WordPressTest/CoreDataTestCase.swift b/WordPress/WordPressTest/CoreDataTestCase.swift index a74a45ecab56..9fb8e71f8393 100644 --- a/WordPress/WordPressTest/CoreDataTestCase.swift +++ b/WordPress/WordPressTest/CoreDataTestCase.swift @@ -1,4 +1,5 @@ import XCTest +import WordPress /// A `XCTestCase` subclass which manages a mock implementation of `CoreDataStack`. Inherit /// from this class to use the `CoreDataStack` mock instance in your test case. @@ -13,3 +14,11 @@ class CoreDataTestCase: XCTestCase { } } + +extension XCTestCase { + + @objc func coreDataStackForTesting() -> CoreDataStack { + ContextManager.forTesting() + } + +} diff --git a/WordPress/WordPressTest/MenusServiceTests.m b/WordPress/WordPressTest/MenusServiceTests.m index 93ca0809c8b1..9095d37d2869 100644 --- a/WordPress/WordPressTest/MenusServiceTests.m +++ b/WordPress/WordPressTest/MenusServiceTests.m @@ -12,7 +12,7 @@ @interface WPAccount () @end @interface MenusServiceTests : XCTestCase -@property (nonatomic, strong) ContextManager *manager; +@property (nonatomic, strong) id manager; @end @implementation MenusServiceTests @@ -20,7 +20,7 @@ @implementation MenusServiceTests - (void)setUp { [super setUp]; - self.manager = [ContextManager forTesting]; + self.manager = [self coreDataStackForTesting]; } - (void)tearDown diff --git a/WordPress/WordPressTest/PostCategoryServiceTests.m b/WordPress/WordPressTest/PostCategoryServiceTests.m index 156b20b96dbb..832eb9f41a36 100644 --- a/WordPress/WordPressTest/PostCategoryServiceTests.m +++ b/WordPress/WordPressTest/PostCategoryServiceTests.m @@ -27,7 +27,7 @@ @implementation PostCategoryServiceForStubbing @interface PostCategoryServiceTests : XCTestCase -@property (nonatomic, strong) ContextManager *manager; +@property (nonatomic, strong) id manager; @property (nonatomic, strong) Blog *blog; @property (nonatomic, strong) PostCategoryServiceForStubbing *service; @@ -39,7 +39,7 @@ - (void)setUp { [super setUp]; - self.manager = [ContextManager forTesting]; + self.manager = [self coreDataStackForTesting]; WordPressComRestApi *api = OCMStrictClassMock([WordPressComRestApi class]); Blog *blog = [ModelTestHelper insertDotComBlogWithContext:self.manager.mainContext]; diff --git a/WordPress/WordPressTest/PostTagServiceTests.m b/WordPress/WordPressTest/PostTagServiceTests.m index 764cff412c60..87291dfb88fd 100644 --- a/WordPress/WordPressTest/PostTagServiceTests.m +++ b/WordPress/WordPressTest/PostTagServiceTests.m @@ -27,7 +27,7 @@ @implementation PostTagServiceForStubbing @interface PostTagServiceTests : XCTestCase -@property (nonatomic, strong) ContextManager *manager; +@property (nonatomic, strong) id manager; @property (nonatomic, strong) Blog *blog; @property (nonatomic, strong) PostTagServiceForStubbing *service; @@ -39,7 +39,7 @@ - (void)setUp { [super setUp]; - self.manager = [ContextManager forTesting]; + self.manager = [self coreDataStackForTesting]; WordPressComRestApi *api = OCMStrictClassMock([WordPressComRestApi class]); Blog *blog = [ModelTestHelper insertDotComBlogWithContext:self.manager.mainContext]; diff --git a/WordPress/WordPressTest/ReaderPostBackupTests.swift b/WordPress/WordPressTest/ReaderPostBackupTests.swift new file mode 100644 index 000000000000..efa0d1b5bd36 --- /dev/null +++ b/WordPress/WordPressTest/ReaderPostBackupTests.swift @@ -0,0 +1,43 @@ +import XCTest +import Nimble + +@testable import WordPress + +final class ReaderPostBackupTests: XCTestCase { + private let storeURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("ReaderPostBackup.sqlite") + private let backupURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("ReaderPostBackup-backup.sqlite") + private var coreDataStack: CoreDataStack! + + override func setUpWithError() throws { + let fileManager = FileManager.default + if fileManager.fileExists(at: storeURL) { + try fileManager.removeItem(at: storeURL) + } + if fileManager.fileExists(at: backupURL) { + try fileManager.removeItem(at: backupURL) + } + + coreDataStack = ContextManager(modelName: ContextManagerModelNameCurrent, store: storeURL) + } + + func testBackupReaderPost() { + coreDataStack.performAndSave { context in + let post: ReaderPost = ReaderPostBuilder(context).build() + let card = ReaderCard(context: context) + card.post = post + post.content = "test post" + post.card = [card] + } + + let migrator = DataMigrator(coreDataStack: coreDataStack, backupLocation: backupURL) + waitUntil { done in + migrator.exportData { result in + XCTAssertNoThrow { + try result.get() + } + done() + } + } + } + +} diff --git a/WordPress/WordPressTest/ReaderPostServiceTest.m b/WordPress/WordPressTest/ReaderPostServiceTest.m index 279a07ef03ab..40f908f40920 100644 --- a/WordPress/WordPressTest/ReaderPostServiceTest.m +++ b/WordPress/WordPressTest/ReaderPostServiceTest.m @@ -1,4 +1,4 @@ -#import "ContextManager.h" +#import "CoreDataStack.h" #import @@ -34,7 +34,7 @@ - (RemoteReaderPost *)remoteReaderPostForTests { } - (void)testDeletePostsWithoutATopic { - id coreDataStack = [ContextManager forTesting]; + id coreDataStack = [self coreDataStackForTesting]; NSManagedObjectContext *context = [coreDataStack mainContext]; ReaderPostService *service = [[ReaderPostService alloc] initWithManagedObjectContext:context]; diff --git a/WordPress/WordPressTest/ReaderPostTest.m b/WordPress/WordPressTest/ReaderPostTest.m index 3d3c5469b7ed..3d59c5a4f156 100644 --- a/WordPress/WordPressTest/ReaderPostTest.m +++ b/WordPress/WordPressTest/ReaderPostTest.m @@ -5,7 +5,7 @@ @interface ReaderPostTest : XCTestCase -@property (nonatomic, strong) ContextManager *coreDataStack; +@property (nonatomic, strong) id coreDataStack; @end @@ -15,7 +15,7 @@ @implementation ReaderPostTest - (void)setUp { - self.coreDataStack = [ContextManager forTesting]; + self.coreDataStack = [self coreDataStackForTesting]; } - (void)tearDown diff --git a/WordPress/WordPressTest/ThemeServiceTests.m b/WordPress/WordPressTest/ThemeServiceTests.m index 4f33fb97460a..a42784e0b4ba 100644 --- a/WordPress/WordPressTest/ThemeServiceTests.m +++ b/WordPress/WordPressTest/ThemeServiceTests.m @@ -16,7 +16,7 @@ @interface WPAccount () #pragma mark - Tests @interface ThemeServiceTests : XCTestCase -@property (nonatomic, strong) ContextManager *manager; +@property (nonatomic, strong) id manager; @end @implementation ThemeServiceTests @@ -24,7 +24,7 @@ @implementation ThemeServiceTests - (void)setUp { [super setUp]; - self.manager = [ContextManager forTesting]; + self.manager = [self coreDataStackForTesting]; } - (void)tearDown diff --git a/WordPress/WordPressTest/WPAccount+ObjCLookupTests.m b/WordPress/WordPressTest/WPAccount+ObjCLookupTests.m index 9a0e073bbe55..ca5cbc10d239 100644 --- a/WordPress/WordPressTest/WPAccount+ObjCLookupTests.m +++ b/WordPress/WordPressTest/WPAccount+ObjCLookupTests.m @@ -3,7 +3,7 @@ @interface WPAccount_ObjCLookupTests : XCTestCase -@property(strong, nonatomic) ContextManager *contextManager; +@property(strong, nonatomic) id contextManager; @end @@ -11,7 +11,7 @@ @implementation WPAccount_ObjCLookupTests - (void) setUp { [super setUp]; - _contextManager = [ContextManager forTesting]; + _contextManager = [self coreDataStackForTesting]; } - (void) testLookupDefaultWordPressComAccountReturnsNilWhenNoAccountIsSet {