Упрощенный базовый стек данных с использованием Mantle, Overcoat и Magical Record в объединении
Я разработал основной стек данных на основе этого поста в блоге (в Swift), где у меня есть два NSManagedObjectContext
экземпляры, контекст главной очереди (NSMainQueueConcurrencyType
) и контекст частной очереди (NSPrivateQueueConcurrencyType
) где основная задача контекста состоит в том, чтобы иметь дело со всеми вещами, связанными с взаимодействием пользователя (редактирование, представление данных пользователю), и единственная задача частного контекста - запись на диск.
Чтобы сделать управление стеком максимально простым, я интегрировал Magical Record, Overcoat и Mantle. Я разделил все это на два класса: одноэлементный стек Core Data (построенный на Magical Record) и одноэлементный менеджер сети (создан на Overcoat, который, в свою очередь, построен на Mantle).
Стек основных данных выглядит следующим образом:
import UIKit
import CoreData
import MagicalRecord
class CoreData: NSObject {
enum StackType: Int {
case Default, AutoMigrating, iCloud, inMemory
}
static let sharedStack = CoreData()
private override init() {}
var type: StackType = .Default
func setupStackWithType(type: StackType, withName name: String = MagicalRecord.defaultStoreName()) {
self.type = type
switch self.type {
case .Default:
MagicalRecord.setupCoreDataStackWithStoreNamed(name)
case .AutoMigrating:
MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed(name)
case .iCloud:
if let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
let url = NSURL(fileURLWithPath: documentsPath)
MagicalRecord.setupCoreDataStackWithiCloudContainer(name, localStoreAtURL: url)
} else {
print("Error: could not find documents directory")
}
case .inMemory:
MagicalRecord.setupCoreDataStackWithInMemoryStore()
}
}
func setupStackWithStoreName(storeName: String, automigrating: Bool = true) {
if automigrating {
MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed(storeName)
} else {
MagicalRecord.setupAutoMigratingCoreDataStack()
}
}
func saveWithBlock(block: (NSManagedObjectContext!) -> ()) {
MagicalRecord.saveWithBlock(block, completion: {
(success, error) in
})
}
func cleanUp() {
MagicalRecord.cleanUp()
}
var managedObjectContext: NSManagedObjectContext {
return NSManagedObjectContext.MR_defaultContext()
}
var privateContext: NSManagedObjectContext {
return NSManagedObjectContext.MR_rootSavingContext()
}
var coordinator: NSPersistentStoreCoordinator {
return NSPersistentStoreCoordinator.MR_defaultStoreCoordinator()
}
var persistentStore: NSPersistentStore {
return NSPersistentStore.MR_defaultPersistentStore()
}
}
Мой сетевой менеджер выглядит так:
import UIKit
import Overcoat
import MTLManagedObjectAdapter
class NetworkManager: OVCManagedHTTPSessionManager {
static let singleton = NetworkManager(baseURL: NSURL(string: Config.ServerBaseEndpoint), managedObjectContext: nil, sessionConfiguration: {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
config.timeoutIntervalForRequest = 15
config.timeoutIntervalForResource = 15
return config
}())
private override init(baseURL url: NSURL?, managedObjectContext context: NSManagedObjectContext?, sessionConfiguration configuration: NSURLSessionConfiguration?) {
super.init(baseURL: url, managedObjectContext: context, sessionConfiguration: configuration)
self.responseSerializer.acceptableContentTypes = ["text/html", "application/json", "application/xml", "image/png"]
self.securityPolicy = AFSecurityPolicy(pinningMode: .None)
self.securityPolicy.allowInvalidCertificates = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: - OVCHTTPSessionManager
override class func modelClassesByResourcePath() -> [String: AnyClass] {
return [Types.RestApi.Post.rawValue:Post.self, "\(Types.RestApi.Post.rawValue)/*": Post.self]
}
}
То, что я не могу полностью обдумать, - это 1) как эти два класса могут работать вместе и 2) в отношении основного стека данных, в каком контексте сохранять, что делать с каким контекстом и т. Д.
За NetworkManager.swift
(который занимает NSManagedObjectContext
для сохранения моделей)
В каком контексте я инициализирую менеджер? Я предполагаю, что если вы сделаете сетевой запрос и JSON преобразуется в промежуточный Mantle
модели и оттуда в NSManagedObejct
экземпляры, эти экземпляры должны быть сохранены прямо в контексте частной очереди, в этом случае полностью обходя контекст основной очереди.
Когда говорим о CoreData.swift
:
1) Волшебная запись имеет saveWithBlock
метод, который создает локальный контекст и распространяет его до корневого контекста (в данном случае корневым контекстом является контекст приватной очереди), но неясно, какую работу следует выполнить внутри блока.
В своей документации они приводят этот пример:
Person *person = ...;
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
Person *localPerson = [person MR_inContext:localContext];
localPerson.firstName = @"John";
localPerson.lastName = @"Appleseed";
}];
где они создают Person
объект за пределами блока сохранения, а затем заново создайте объект в локальном контексте, а затем отредактируйте его свойства. Однако в моем случае все Person
объекты будут экземпляром MTLModel
не NSManagedObject
, Когда я создаю некоторый объект модели, поскольку он не является базовым объектом данных, он не будет вставлен в какой-либо контекст, пока я не использую MTLManagedObjectAdapter
превратить модель в NSManagedObject
пример.
Кажется, лучший способ сделать это - создать MTLModel
экземпляр (ы), сделайте все необходимое для редактирования, а затем либо 1) внутри saveWithBlock
вставить вновь созданные управляемые объекты прямо в локальный контекст и позволить ему распространяться вверх или 2) вставить объекты в контекст приватной очереди и сохранить.
2) Мне действительно нужно использовать контекст основной очереди вообще для сохранения и редактирования? Как я уже говорил, Mantle имеет классы моделей в качестве подкласса MTLModel
а позже отображает их в NSManagedObject
экземпляры, так что имеет смысл, что я мог бы просто сохранить непосредственно в контекст приватной очереди (чья единственная работа в любом случае заключается в записи на диск)
3) Если мне не нужно использовать контекст основной очереди для сохранения / редактирования, не станет ли он контекстом, который я использую для извлечения NSManagedObjects
(учитывая, что работа Частной очереди заключается в записи на диск, а функции сохранения / редактирования контекста главной очереди, похоже, устарели из-за промежуточной структуры модели Mantle)?