5 // Created by Hori,Masaki on 2017/02/22.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
13 struct MappingConfiguration<T: Entity> {
16 let dataKeys: [String]
17 let primaryKeys: [String]
18 let editorStore: CoreDataAccessor
19 let ignoreKeys: [String]
22 dataKeys: [String] = ["api_data"],
23 primaryKeys: [String] = ["id"],
24 editorStore: CoreDataAccessor,
25 ignoreKeys: [String] = []) {
28 self.dataKeys = dataKeys
29 self.primaryKeys = primaryKeys
30 self.editorStore = editorStore
31 self.ignoreKeys = ignoreKeys
37 associatedtype ObjectType: Entity
39 init(_ apiResponse: APIResponse)
41 var apiResponse: APIResponse { get }
42 var configuration: MappingConfiguration<ObjectType> { get }
44 func registerElement(_ element: JSON, to object: ObjectType)
46 func beginRegister(_ object: ObjectType)
47 func handleExtraValue(_ value: JSON, forKey key: String, to object: ObjectType) -> Bool
48 func finishOperating()
52 // delete api_ prefix.
53 func keyByDeletingPrefix() -> String {
60 return String(self[index(startIndex, offsetBy: 4)...])
64 extension JSONMapper {
66 var data: JSON { return apiResponse.json[configuration.dataKeys] }
68 private func isEqual(_ lhs: AnyObject?, _ rhs: AnyObject?) -> Bool {
70 if lhs == nil, rhs == nil {
74 if let lhs = lhs, let rhs = rhs {
76 return lhs.isEqual(rhs)
82 func setValueIfNeeded(_ value: JSON, to object: ObjectType, forKey key: String) {
84 var validValue = value.object as AnyObject?
87 try object.validateValue(&validValue, forKey: key)
94 let old = object.value(forKey: key)
95 if !isEqual(old as AnyObject?, validValue) {
97 object.setValue(validValue, forKey: key)
101 func registerElement(_ element: JSON, to object: ObjectType) {
103 beginRegister(object)
104 element.forEach { (key, value) in
106 if configuration.ignoreKeys.contains(key) {
110 if handleExtraValue(value, forKey: key, to: object) {
117 value.array?.enumerated().forEach {
119 let newKey = "\(key)_\($0.offset)"
120 setValueIfNeeded($0.element, to: object, forKey: newKey)
124 value.forEach { (subKey: String, subValue) in
126 let newKey = "\(key)_D_\(subKey.keyByDeletingPrefix())"
127 setValueIfNeeded(subValue, to: object, forKey: newKey)
131 setValueIfNeeded(value, to: object, forKey: key)
137 private var sortDescriptors: [NSSortDescriptor] {
139 return configuration.primaryKeys.map { NSSortDescriptor(key: $0, ascending: true) }
142 private func objectSearch(_ objects: [ObjectType], _ element: JSON) -> ObjectType? {
144 let keyPiar = configuration.primaryKeys.map { (key: $0, apiKey: "api_\($0)") }
146 return objects.binarySearch {
148 // TODO: replace to forEach
149 for piar in keyPiar {
151 guard let v1 = $0.value(forKey: piar.key) else {
153 return .orderedAscending
156 if element[piar.apiKey].type == .null {
158 return .orderedDescending
161 let v2 = element[piar.apiKey].object
163 return (v1 as AnyObject).compare(v2)
166 return .orderedDescending
170 private func sortedObjects<ResultType: Entity>(_ entity: ResultType.Type) -> [ResultType] {
172 let store = configuration.editorStore
174 guard let objects = try? store.objects(of: configuration.entity) else {
176 Logger.shared.log("Can not get entity named \(configuration.entity)")
181 return (objects as NSArray).sortedArray(using: sortDescriptors) as? [ResultType] ?? []
183 private func commintInContext() {
185 let store = configuration.editorStore
186 let objects = sortedObjects(configuration.entity)
188 let list = (data.type == .array ? data.arrayValue : [data])
191 if let object = objectSearch(objects, $0) {
193 registerElement($0, to: object)
195 } else if let new = store.insertNewObject(for: configuration.entity) {
197 registerElement($0, to: new)
201 fatalError("Can not get entity named \(configuration.entity)")
211 configuration.editorStore
214 self.commintInContext()
218 func beginRegister(_ object: ObjectType) {}
220 func handleExtraValue(_ value: JSON, forKey key: String, to object: ObjectType) -> Bool {
225 func finishOperating() {}