在 MVVM 应用程序中访问核心数据堆栈
Accessing Core Data Stack in MVVM application
我正在使用 MVVM
模式编写应用程序。我想知道如何创建 CoreData
堆栈,以便可以从我的应用程序中的不同位置访问它。
第一种方法是在 AppDelegate
中创建一个持久容器,然后将此服务注入我的 ViewModel(同时将 managedObjectContext
作为环境变量传递给我的视图)。
但是,通过这种方式访问整个应用程序的上下文更加困难:例如在解码网络响应时,因为它们无法访问 managedObjectContext
:
protocol APIResource {
associatedtype Response: Decodable
...
}
extension APIResource {
func decode(_ data: Data) -> AnyPublisher<Response, APIError> {
Just(data)
// how can I access context here to pass it to JSONDecoder?
.decode(type: Response.self, decoder: JSONDecoder())
.mapError { error in
.parsing(description: error.localizedDescription)
}
.eraseToAnyPublisher()
}
}
我见过的另一种解决方案是使用单例。我可以从项目中的任何地方访问它,但如何才能以正确的方式创建它?
如果我不想同时修改 main 和 background 队列中的某些对象怎么办?或者如果两个队列都想修改同一个对象怎么办?
你可以使用 Core Data Singleton class
import CoreData
class CoreDataStack {
static let shared = CoreDataStack()
private init() {}
var managedObjectContext: NSManagedObjectContext {
return self.persistentContainer.viewContext
}
var workingContext: NSManagedObjectContext {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.parent = self.managedObjectContext
return context
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "MyStuff")
container.loadPersistentStores(completionHandler: { storeDescription, error in
if let error = error as NSError? {
RaiseError.raise()
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext() {
self.managedObjectContext.performAndWait {
if self.managedObjectContext.hasChanges {
do {
try self.managedObjectContext.save()
appPrint("Main context saved")
} catch {
appPrint(error)
RaiseError.raise()
}
}
}
}
func saveWorkingContext(context: NSManagedObjectContext) {
do {
try context.save()
appPrint("Working context saved")
saveContext()
} catch (let error) {
appPrint(error)
RaiseError.raise()
}
}
}
核心数据不是线程安全的。如果您在 manageObject 上写了一些东西并且不想保存它,但是其他线程保存了上下文,那么您不想保留的更改也会保留。
因此,为避免这种情况,请始终创建工作上下文 - 这是私有的。
当您按下保存时,首先保存私有上下文,然后保存主上下文。
在 MVVM 中,您应该有 DataLayer,您的 ViewModel 通过它与 Core Data 单例交互 class。
我正在使用 MVVM
模式编写应用程序。我想知道如何创建 CoreData
堆栈,以便可以从我的应用程序中的不同位置访问它。
第一种方法是在 AppDelegate
中创建一个持久容器,然后将此服务注入我的 ViewModel(同时将 managedObjectContext
作为环境变量传递给我的视图)。
但是,通过这种方式访问整个应用程序的上下文更加困难:例如在解码网络响应时,因为它们无法访问 managedObjectContext
:
protocol APIResource {
associatedtype Response: Decodable
...
}
extension APIResource {
func decode(_ data: Data) -> AnyPublisher<Response, APIError> {
Just(data)
// how can I access context here to pass it to JSONDecoder?
.decode(type: Response.self, decoder: JSONDecoder())
.mapError { error in
.parsing(description: error.localizedDescription)
}
.eraseToAnyPublisher()
}
}
我见过的另一种解决方案是使用单例。我可以从项目中的任何地方访问它,但如何才能以正确的方式创建它?
如果我不想同时修改 main 和 background 队列中的某些对象怎么办?或者如果两个队列都想修改同一个对象怎么办?
你可以使用 Core Data Singleton class
import CoreData
class CoreDataStack {
static let shared = CoreDataStack()
private init() {}
var managedObjectContext: NSManagedObjectContext {
return self.persistentContainer.viewContext
}
var workingContext: NSManagedObjectContext {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.parent = self.managedObjectContext
return context
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "MyStuff")
container.loadPersistentStores(completionHandler: { storeDescription, error in
if let error = error as NSError? {
RaiseError.raise()
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext() {
self.managedObjectContext.performAndWait {
if self.managedObjectContext.hasChanges {
do {
try self.managedObjectContext.save()
appPrint("Main context saved")
} catch {
appPrint(error)
RaiseError.raise()
}
}
}
}
func saveWorkingContext(context: NSManagedObjectContext) {
do {
try context.save()
appPrint("Working context saved")
saveContext()
} catch (let error) {
appPrint(error)
RaiseError.raise()
}
}
}
核心数据不是线程安全的。如果您在 manageObject 上写了一些东西并且不想保存它,但是其他线程保存了上下文,那么您不想保留的更改也会保留。
因此,为避免这种情况,请始终创建工作上下文 - 这是私有的。
当您按下保存时,首先保存私有上下文,然后保存主上下文。
在 MVVM 中,您应该有 DataLayer,您的 ViewModel 通过它与 Core Data 单例交互 class。