在 MVVM 应用程序中访问核心数据堆栈

Posted

技术标签:

【中文标题】在 MVVM 应用程序中访问核心数据堆栈【英文标题】:Accessing Core Data Stack in MVVM application 【发布时间】:2020-05-03 09:02:18 【问题描述】:

我正在使用MVVM 模式编写应用程序。我想知道如何创建 CoreData 堆栈,以便可以从我的应用程序中的各个位置访问它。

第一种方法是在AppDelegate 中创建一个持久容器,然后将此服务注入我的 ViewModel(同时将managedObjectContext 作为环境变量传递给我的 View)。

但是,通过这种方式访问​​整个应用程序的上下文更加困难:例如在解码网络响应时,因为他们无权访问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()
    

我见过的另一个解决方案是使用单例。我可以从项目中的任何位置访问它,但我如何以正确的方式创建它

如果我不想同时修改 mainbackground 队列中的某个对象怎么办?或者如果两个队列都想修改同一个对象怎么办?

【问题讨论】:

不是一个实际的答案,而是思考的食物:恕我直言,在解码网络响应时使用核心数据不是正确的方法。我宁愿使用DTOs,然后将转换和存储它们的责任留给其他人。 对于那些想知道如何将上下文注入 View 和 View Model,***.com/q/63959960/2226315 【参考方案1】:

你可以使用 Core Data Singleton 类

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()
        
    

Core Data 不是线程安全的。如果你在 manageObject 上写了一些东西并且不想保存它,但是其他一些线程保存了上下文,那么你不想保留的更改也会保留。

因此,为了避免这种情况,请始终创建工作上下文 - 这是私有的。

当你按下保存时,第一个私有上下文被保存,然后你保存主上下文。

在 MVVM 中,您应该有 DataLayer,您的 ViewModel 通过它与 Core Data 单例类进行交互。

【讨论】:

我们如何在 SwiftUI 中使用它?您是仅通过 EnvironmentObject 传递上下文还是传递整个 CoreDataStack?谢谢!

以上是关于在 MVVM 应用程序中访问核心数据堆栈的主要内容,如果未能解决你的问题,请参考以下文章

WPF/MVVM 应用程序中的数据访问使用啥模式

GDB核心转储具有损坏的堆栈,显示“堆栈帧无法访问地址0x12处的内存”

在单元测试中构建核心数据堆栈时出错

尝试使用依赖注入设置核心数据堆栈

核心数据在没有堆栈的情况下工作

如何在 try catch 语句中包装 MVVM Light ViewModel?