5 // Created by Hori,Masaki on 2017/02/22.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
12 struct MappingConfiguration<T: NSManagedObject> {
14 let dataKeys: [String]
15 let primaryKey: String
16 let compositPrimaryKeys: [String]?
17 let editorStore: CoreDataAccessor
18 let ignoreKeys: [String]
20 init(entity: Entity<T>,
21 dataKeys: [String] = ["api_data"],
22 primaryKey: String = "id",
23 compositPrimaryKeys: [String]? = nil,
24 editorStore: CoreDataAccessor,
25 ignoreKeys: [String] = []) {
27 self.dataKeys = dataKeys
28 self.primaryKey = primaryKey
29 self.compositPrimaryKeys = compositPrimaryKeys
30 self.editorStore = editorStore
31 self.ignoreKeys = ignoreKeys
36 associatedtype ObjectType: NSManagedObject
38 init(_ apiResponse: APIResponse)
40 var apiResponse: APIResponse { get }
41 var configuration: MappingConfiguration<ObjectType> { get }
43 func registerElement(_ element: JSON, to object: ObjectType)
45 func beginRegister(_ object: ObjectType)
46 func handleExtraValue(_ value: JSON, forKey key: String, to object: ObjectType) -> Bool
47 func finishOperating()
51 func keyByDeletingPrefix() -> String {
52 if self.characters.count < 5 { return self }
53 let s = self.index(self.startIndex, offsetBy: 4)
54 return self[s..<self.endIndex]
58 extension JSONMapper {
59 var data: JSON { return apiResponse.json[configuration.dataKeys] }
61 private func isEqual(_ lhs: AnyObject?, _ rhs: AnyObject?) -> Bool {
62 if lhs == nil, rhs == nil { return true }
63 if let lhs = lhs, let rhs = rhs { return lhs.isEqual(rhs) }
66 func setValueIfNeeded(_ value: JSON, to object: ObjectType, forKey key: String) {
67 var validValue = value.object as AnyObject?
69 try object.validateValue(&validValue, forKey: key)
74 let old = object.value(forKey: key)
75 if !isEqual(old as AnyObject?, validValue) {
76 object.willChangeValue(forKey: key)
77 object.setValue(validValue, forKey: key)
78 object.didChangeValue(forKey: key)
82 func registerElement(_ element: JSON, to object: ObjectType) {
84 element.forEach { (key: String, value: JSON) in
85 if configuration.ignoreKeys.contains(key) { return }
86 if handleExtraValue(value, forKey: key, to: object) { return }
89 value.array?.enumerated().forEach {
90 let newKey = "\(key)_\($0.offset)"
91 setValueIfNeeded($0.element, to: object, forKey: newKey)
94 value.forEach { (subKey: String, subValue) in
95 let newKey = "\(key)_D_\(subKey.keyByDeletingPrefix())"
96 setValueIfNeeded(subValue, to: object, forKey: newKey)
99 setValueIfNeeded(value, to: object, forKey: key)
103 private var sortDescriptors: [NSSortDescriptor] {
104 let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey]
105 return keys.map { NSSortDescriptor(key: $0, ascending: true) }
107 private func objectSearch(_ objects: [ObjectType], _ element: JSON) -> ObjectType? {
108 let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey]
109 let keyPiar = keys.map { (key: $0, apiKey: "api_\($0)") }
110 return objects.binarySearch {
111 for piar in keyPiar {
112 guard let v1 = $0.value(forKey: piar.key)
113 else { return .orderedAscending }
114 if element[piar.apiKey].type == .null { return .orderedDescending }
115 let v2 = element[piar.apiKey].object
116 return (v1 as AnyObject).compare(v2)
118 return .orderedDescending
122 let store = configuration.editorStore
123 guard let objects = try? store.objects(with: configuration.entity, sortDescriptors: sortDescriptors)
124 else { return print("Can not get entity named \(configuration.entity.name)") }
125 let list = (data.type == .array ? data.arrayValue : [data])
127 if let object = objectSearch(objects, $0) {
128 registerElement($0, to: object)
129 } else if let new = store.insertNewObject(for: configuration.entity) {
130 registerElement($0, to: new)
132 fatalError("Can not get entity named \(configuration.entity.name)")
136 store.saveActionCore()
139 func beginRegister(_ object: ObjectType) {}
140 func handleExtraValue(_ value: JSON, forKey key: String, to object: ObjectType) -> Bool {
143 func finishOperating() {}