Code Monkey home page Code Monkey logo

coredatarules's Introduction

Core Data Rules

В этом приложении мы рассматриваем как правильно работать с Core Data

Rules

1. Работайте с Core Data всегда в нужном контексте.

Все что вы делаете на главном потоке должно происходить на главном контексте mainContext или viewContext. Все что вы делаете или хотите сделать в бэкграунд потоке вы должны делать в privateContext

Пример

Model.coreData.backgroundTask { privateContext in
            
    privateContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
    
    for data in array {
        
        let cdUser = CDUser(context: privateContext)
        cdUser.fill(data: data)
        
    }
    
    Model.coreData.save(in: privateContext) { status in
        
        DispatchQueue.main.async {
            
            switch status {
            case .hasNoChanges, .saved:
                completion(nil)
            default:
                completion(StorageError.saveFailure(CDUser.entityName).NSError)
            }
            
        }
        
    }
            
}

В этом примере мы сделали backgroundTask и получили privateContext. Далее мы создаем пользователя на этом контексте, сохраняем и переходим на главный поток.

2. В Core Data запрещено передавать объекты между контекстами напрямую

Допустим вы создали экран редактирования объекта с viewContext. На этом экране вы нажимаете кнопку и открывается новый экран список объектов для выбора на ваш экран. Допустим экран где список выбора тоже есть viewContext но это другой viewContext. Так вот запрещено передавать объект с разных контекстов напрямую. Вместо передачи самого объекта нужно передавать objectID.

Пример

func updateUser(cdUser: CDUser) {
        
    guard let cdUserInContext = self.viewContext.object(with: cdUser.objectID) as? CDUser else { return }

    self.cdPost.cdUser = cdUserInContext

    self.updateUserCompletion?()

}

В этом примере мы передали CDUser но мы не обращаемся к его свойствам и не присваиваем его как параметр как свойство CDPost. Вместо этого мы получаем objectID и воссоздаем объект на нужном нам контексте и только после этого мы можем присвоить пользователя для модели CDPost.

3. MergeConflict при сохранении

3.1 Когда вы сохранили что то в privateContext работая в это же время на viewContext

Предположим вы работаете на экране редактирования вашей модели CDPost и вы используете viewContext. В это время прилетел пуш и у вас где то сработал код и в privateContext обновил вашу модель CDPost. В этом случае когда вы будете сохранить ваш viewContext у вас будет ошибка Error Domain=App.NSError Code=0 "Could not merge changes." UserInfo={NSLocalizedDescription=Could not merge changes.}

Решение 1

Установить для вашего viewContext свойство self.viewContext.automaticallyMergesChangesFromParent = true. Тогда когда сохраниться privateContext все изменения обновятся на вашем viewContext.

Решение 2

Установить для вашего viewContext свойство self.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump. Тогда когда будет сохранятся viewContext то он перезапишет изменения сделанные на privateContext, при этом изменения на privateContext сохранятся если вы их не меняли на viewContext.

Решение 3

Перед сохранением вашего viewContext нужно обновить контекст self.viewContext.refreshAllObjects() или self.viewContext.refresh(self.cdPost, mergeChanges: true)

3.2 Когда вы сохранили что то в privateContext работая в это же время на viewContext. Тут есть дополнение когда вы сохранили используя BatchResuest.

Model.coreData.backgroundTask { privateContext in
            
            guard let cdPost = privateContext.object(with: cdPost.objectID) as? CDPost else { return }
            
            guard let id = cdPost.id else { return }
            
            let predicate = NSPredicate(format: "id == %@", id)
            
            let updateRequest = NSBatchUpdateRequest(entityName: CDPost.entityName)
            updateRequest.predicate = predicate
            updateRequest.propertiesToUpdate = ["mark": !cdPost.mark]
            updateRequest.resultType = .updatedObjectIDsResultType
            
            do {
                let results = try privateContext.execute(updateRequest) as! NSBatchUpdateResult
                let changes: [AnyHashable: Any] = [
                    NSUpdatedObjectsKey: results.result as! [NSManagedObjectID]
                ]
                
                completion(nil)
            } catch {
                completion(error.NSError)
            }
            
        }

Решение 1

Установить для вашего viewContext свойство self.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump. Тогда когда будет сохранятся viewContext то он перезапишет изменения сделанные на privateContext, при этом изменения на privateContext сохранятся если вы их не меняли на viewContext.

Решение 2

Когда вы делаете BatchResuest то это пишется сразу в базу данных миную Notification и automaticallyMergesChangesFromParent в этом случае на работает. Нужно сделать Merge вручную

Model.coreData.backgroundTask { privateContext in
            
            guard let cdPost = privateContext.object(with: cdPost.objectID) as? CDPost else { return }
            
            guard let id = cdPost.id else { return }
            
            let predicate = NSPredicate(format: "id == %@", id)
            
            let updateRequest = NSBatchUpdateRequest(entityName: CDPost.entityName)
            updateRequest.predicate = predicate
            updateRequest.propertiesToUpdate = ["mark": !cdPost.mark]
            updateRequest.resultType = .updatedObjectIDsResultType
            
            do {
                let results = try privateContext.execute(updateRequest) as! NSBatchUpdateResult
                let changes: [AnyHashable: Any] = [
                    NSUpdatedObjectsKey: results.result as! [NSManagedObjectID]
                ]
                if !contexts.isEmpty {
                    Log.debug("CoreData", "start merging")
                    NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: contexts)
                    Log.debug("CoreData", "end merging")
                }
                completion(nil)
            } catch {
                completion(error.NSError)
            }
            
        }

Обратите тут внимание на код

if !contexts.isEmpty {
            Log.debug("CoreData", "start merging")
            NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: contexts)
            Log.debug("CoreData", "end merging")
}

Мы передаем массив контектов которые мы хотим уведомить об изменениям. В этом массиве должен быть наш viewContext.

coredatarules's People

Contributors

sapgv avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.