Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CoreData, custom merge policy

GDG Cherkasy
January 26, 2019

CoreData, custom merge policy

Agenda
Prehistory. (Constraints, background context, network and JSON decoding)
What is that a merge policy?
Base merge policies
How conflicts merged for different policies
Custom merge policy

GDG Cherkasy

January 26, 2019
Tweet

More Decks by GDG Cherkasy

Other Decks in Technology

Transcript

  1. Agenda Prehistory. (Constraints, background context, network and json decoding) What

    is that a merge policy? Base merge policies How conflicts merged for different policies Custom merge policy
  2. What is that a merge policy? A policy object that

    you use to resolve conflicts between the persistent store and in-memory versions of managed objects. A conflict is a mismatch between state held at two different layers in the Core Data stack. A conflict can arise when you save a managed object context and you have stale data at another layer. There are two places in which a conflict may occur: • Between the managed object context layer and its in-memory cached state at the persistent store coordinator layer. • Between the cached state at the persistent store coordinator and the external store (file, database, and so forth). Conflicts are represented by instances of NSMergeConflict.
  3. Rollback A policy that merges conflicts between the persistent store's

    version of the object and the current in-memory version by discarding all state for the changed objects in conflict.
  4. Overwrite A policy that merges conflicts between the persistent store's

    version of the object and the current in-memory version by pushing the entire in-memory object to the persistent store.
  5. MergeByPropertyObjectTrump A policy that merges conflicts between the persistent store's

    version of the object and the current in-memory version by individual property, with the external changes trumping in-memory changes.
  6. MergeByPropertyStoreTrump A policy that merges conflicts between the persistent store's

    version of the object and the current in-memory version by individual property, with the in-memory changes trumping external changes.
  7. Decodable initialize public class Item: NSManagedObject, Decodable { enum CodingKeys:

    String, CodingKey { case id . . . . } public required convenience init(from decoder: Decoder) throws { let context = decoder.userInfo[CodingUserInfoKey.context!] as! NSManagedObjectContext let container = try decoder.container(keyedBy: CodingKeys.self) self.init(context: context) if let intID = try container.decodeIfPresent(Int32.self, forKey: .id) { self.id = intID } . . . .
  8. Persistent container setup lazy var persistentContainer: NSPersistentContainer = { let

    container = NSPersistentContainer(name: "MergePolicyExample") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = <Some_default_merge_policy> }) return container }()
  9. Test data example [ { "id": 0, "name": "Default name

    0", "info": "Default info 0" }, { "id": 1, "name": "Default name 1", "info": "Default info 1" }, { "id": 2, "name": "Default name 2", "info": "Default info 2" }, . . . . . ] [ { "id": 0, "name": "Updated name 0", "info": "Updated info 0" }, { "id": 1, "name": "Updated name 1", "info": null }, { "id": 2, "name": "Updated name 2", }, . . . . . ]
  10. Update data in view context private func refresh() { let

    data = NSDataAsset(name: "update")!.data let decoder = JSONDecoder() decoder.userInfo[CodingUserInfoKey.context!] = container.viewContext do { _ = try decoder.decode([Item].self, from: data) try container.viewContext.save() } catch { } .... }
  11. Update data in background context private func refresh() { let

    data = NSDataAsset(name: "update")!.data container.performBackgroundTask { [weak self] (context) in context.mergePolicy = <Some merge policy> let decoder = JSONDecoder() decoder.userInfo[CodingUserInfoKey.context!] = context do { _ = try decoder.decode([Item].self, from: data) try context.save() } catch { } .... } }
  12. NSMergePolicy interface open class NSMergePolicy : NSObject { open var

    mergeType: NSMergePolicyType { get } public init(merge ty: NSMergePolicyType) open func resolve(mergeConflicts list: [Any]) throws open func resolve(optimisticLockingConflicts list: [NSMergeConflict]) throws open func resolve(constraintConflicts list: [NSConstraintConflict]) throws }
  13. override func resolve(constraintConflicts list: [NSConstraintConflict]) throws { for conflict in

    list { guard let databaseObject = conflict.databaseObject else { try super.resolve(constraintConflicts: list) return } let allKeys = databaseObject.entity.attributesByName.keys for conflictObject in conflict.conflictingObjects { let changedKeys = conflictObject.changedValues().keys let keys = allKeys.filter { !changedKeys.contains($0) } for key in keys { let value = databaseObject.value(forKey: key) conflictObject.setValue(value, forKey: key) } } } try super.resolve(constraintConflicts: list) }
  14. class func create() -> MergePolicy { return MergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType) }

    private override init(merge ty: NSMergePolicyType) { super.init(merge: ty) }