5 // Created by Hori,Masaki on 2017/02/22.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
11 struct MappingConfiguration<T: NSManagedObject> {
14 let primaryKey: String
15 let compositPrimaryKeys: [String]?
16 let editorStore: CoreDataAccessor
17 let ignoreKeys: [String]
19 init(entity: Entity<T>,
20 dataKey: String = "api_data",
21 primaryKey: String = "id",
22 compositPrimaryKeys: [String]? = nil,
23 editorStore: CoreDataAccessor,
24 ignoreKeys: [String] = []) {
26 self.dataKey = dataKey
27 self.primaryKey = primaryKey
28 self.compositPrimaryKeys = compositPrimaryKeys
29 self.editorStore = editorStore
30 self.ignoreKeys = ignoreKeys
35 associatedtype ObjectType: NSManagedObject
37 init(_ apiResponse: APIResponse)
39 var apiResponse: APIResponse { get }
40 var configuration: MappingConfiguration<ObjectType> { get }
42 func registerElement(_ element: [String: Any], to object: ObjectType)
44 func beginRegister(_ object: ObjectType)
45 func handleExtraValue(_ value: Any, forKey key: String, to object: ObjectType) -> Bool
46 func finishOperating()
50 func keyByDeletingPrefix() -> String {
51 if self.characters.count < 5 { return self }
52 let s = self.index(self.startIndex, offsetBy: 4)
53 return self[s..<self.endIndex]
57 extension JSONMapper {
58 private func isEqual(_ a: AnyObject?, _ b: AnyObject?) -> Bool {
59 if a == nil, b == nil { return true }
60 if let aa = a, let bb = b { return aa.isEqual(bb) }
63 func setValueIfNeeded(_ value: AnyObject?, to object: ObjectType, forKey key: String) {
64 var validValue = value
65 do { try object.validateValue(&validValue, forKey: key) }
68 let old = object.value(forKey: key)
69 if !isEqual(old as AnyObject?, validValue) {
70 object.willChangeValue(forKey: key)
71 object.setValue(validValue, forKey: key)
72 object.didChangeValue(forKey: key)
76 func registerElement(_ element: [String: Any], to object: ObjectType) {
78 element.forEach { (key: String, value: Any) in
79 if configuration.ignoreKeys.contains(key) { return }
80 if handleExtraValue(value, forKey: key, to: object) { return }
82 case let a as [AnyObject]:
83 a.enumerated().forEach {
84 let newKey = "\(key)_\($0.offset)"
85 setValueIfNeeded($0.element, to: object, forKey: newKey)
87 case let d as [String: AnyObject]:
88 d.forEach { (subKey: String, subValue) in
89 let newKey = "\(key)_D_\(subKey.keyByDeletingPrefix())"
90 setValueIfNeeded(subValue, to: object, forKey: newKey)
93 setValueIfNeeded(value as AnyObject?, to: object, forKey: key)
97 private var sortDescriptors: [NSSortDescriptor] {
98 let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey]
99 return keys.map { NSSortDescriptor(key: $0, ascending: true) }
101 private func objectSearch(_ objects: [ObjectType], _ element: [String: Any]) -> ObjectType? {
102 let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey]
103 let keyPiar = keys.map { (key: $0, apiKey: "api_\($0)") }
104 return objects.binarySearch {
105 for piar in keyPiar {
106 guard let v1 = $0.value(forKey: piar.key)
107 else { return .orderedAscending }
108 guard let v2 = element[piar.apiKey]
109 else { return .orderedDescending }
110 return (v1 as AnyObject).compare(v2)
112 return .orderedDescending
116 guard var d = (apiResponse.json as AnyObject).value(forKeyPath: configuration.dataKey)
117 else { return print("JSON is wrong.") }
118 if let dd = d as? NSDictionary { d = [dd] }
119 guard let data = d as? [[String: Any]]
120 else { return print("JSON is wrong.") }
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)") }
126 if let object = objectSearch(objects, $0) {
127 registerElement($0, to: object)
128 } else if let new = store.insertNewObject(for: configuration.entity) {
129 registerElement($0, to: new)
131 fatalError("Can not get entity named \(configuration.entity.name)")
135 store.saveActionCore()
139 func beginRegister(_ object: ObjectType) {}
140 func handleExtraValue(_ value: Any, forKey key: String, to object: ObjectType) -> Bool {
143 func finishOperating() {}