Доступ к базовому стеку данных в приложении MVVM - PullRequest
0 голосов
/ 03 мая 2020

Я пишу приложение, используя шаблон MVVM. И я не знаю, как лучше всего получить доступ к Core Data стеку из разных мест в моем приложении.

Первый подход заключается в использовании внедрения зависимостей: в AppDelegate создайте постоянный контейнер, передайте его CoreDataService а затем внедрить этот сервис в мои ViewModels (одновременно передать managedObjectContext как переменную окружения в мои Views). Таким образом, однако, доступ к контексту во всем приложении более сложен: например, при декодировании сетевых ответов, поскольку у них нет доступа к 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()
    }
}

Другое решение, которое я видел, состоит в том, чтобы использовать синглтон. Я могу получить к нему доступ из любой точки проекта, позвонив по номеру CoreDataStack.shared.context. Но это хорошее решение?

А что, если я не хочу изменять какой-либо объект в основной и фоновой очереди одновременно? Я думаю, что если я сохраню объекты в одной очереди и прочту их в другой, это не будет проблемой. Но что, если обе очереди захотят изменить их?

1 Ответ

1 голос
/ 03 мая 2020

Вы можете использовать класс CoreData Singleton

import CoreData

class CoreDataStack {

    static let shared = CoreDataStack()

    private init(){

    }

    var managedObjectContext: NSManagedObjectContext { get {
        return self.persistentContainer.viewContext
        }
    }

    var workingContext: NSManagedObjectContext { get {
        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()
        }
    }


}

CoreData не является потокобезопасным ... если вы пишете что-то в manageObject и не хотите его сохранять, но какой-то другой поток сохраняет контекст ... тогда изменения, которые вы не хотите сохранять ... также будут сохраняться. Поэтому, чтобы избежать этой ситуации, всегда создавайте рабочий контекст ... который является приватным, когда вы нажимаете сохранить ... затем сначала сохраняется приватный контекст, а затем вы сохраняете основной контекст ...

в MVVM у вас должен быть dataLayer, через который ваш viewModal взаимодействует с одноэлементным классом coreData

...