5 // Created by Hori,Masaki on 2017/02/22.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
11 struct MappingConfiguration {
12 let entityType: NSManagedObject.Type
14 let primaryKey: String
15 let compositPrimaryKeys: [String]?
16 let editorStore: CoreDataAccessor
17 let ignoreKeys: [String]
19 init(entityType: NSManagedObject.Type,
20 dataKey: String = "api_data",
21 primaryKey: String = "id",
22 compositPrimaryKeys: [String]? = nil,
23 editorStore: CoreDataAccessor,
24 ignoreKeys: [String] = []) {
25 self.entityType = entityType
26 self.dataKey = dataKey
27 self.primaryKey = primaryKey
28 self.compositPrimaryKeys = compositPrimaryKeys
29 self.editorStore = editorStore
30 self.ignoreKeys = ignoreKeys
35 init(_ apiResponse: APIResponse)
37 var apiResponse: APIResponse { get }
38 var configuration: MappingConfiguration { get }
40 func registerElement(_ element: [String: Any], to object: NSManagedObject)
42 func beginRegister(_ object: NSManagedObject)
43 func handleExtraValue(_ value: Any, forKey key: String, to object: NSManagedObject) -> Bool
44 func finishOperating()
48 func keyByDeletingPrefix() -> String {
49 if self.characters.count < 5 { return self }
50 let s = self.index(self.startIndex, offsetBy: 4)
51 return self[s..<self.endIndex]
55 extension JSONMapper {
56 private func isEqual(_ a: AnyObject?, _ b: AnyObject?) -> Bool {
57 if a == nil, b == nil { return true }
58 if let aa = a, let bb = b { return aa.isEqual(bb) }
61 func setValueIfNeeded(_ value: AnyObject?, to object: NSManagedObject, forKey key: String) {
62 var validValue = value
63 do { try object.validateValue(&validValue, forKey: key) }
66 let old = object.value(forKey: key)
67 if !isEqual(old as AnyObject?, validValue) {
68 object.willChangeValue(forKey: key)
69 object.setValue(validValue, forKey: key)
70 object.didChangeValue(forKey: key)
74 func registerElement(_ element: [String: Any], to object: NSManagedObject) {
76 element.forEach { (key: String, value: Any) in
77 if configuration.ignoreKeys.contains(key) { return }
78 if handleExtraValue(value, forKey: key, to: object) { return }
80 case let a as [AnyObject]:
81 a.enumerated().forEach {
82 let newKey = "\(key)_\($0.offset)"
83 setValueIfNeeded($0.element, to: object, forKey: newKey)
85 case let d as [String: AnyObject]:
86 d.forEach { (subKey: String, subValue) in
87 let newKey = "\(key)_D_\(subKey.keyByDeletingPrefix())"
88 setValueIfNeeded(subValue, to: object, forKey: newKey)
91 setValueIfNeeded(value as AnyObject?, to: object, forKey: key)
95 private var sortDescriptors: [NSSortDescriptor] {
96 let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey]
97 return keys.map { NSSortDescriptor(key: $0, ascending: true) }
99 private func objectSearch(_ objects: [NSManagedObject], _ element: [String: Any]) -> NSManagedObject? {
100 let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey]
101 let keyPiar = keys.map { (key: $0, apiKey: "api_\($0)") }
102 return objects.binarySearch {
103 for piar in keyPiar {
104 guard let v1 = $0.value(forKey: piar.key)
105 else { return .orderedAscending }
106 guard let v2 = element[piar.apiKey]
107 else { return .orderedDescending }
108 return (v1 as AnyObject).compare(v2)
110 return .orderedDescending
114 guard var d = (apiResponse.json as AnyObject).value(forKeyPath: configuration.dataKey)
115 else { return print("JSON is wrong.") }
116 if let dd = d as? NSDictionary { d = [dd] }
117 guard let data = d as? [[String: Any]]
118 else { return print("JSON is wrong.") }
120 let store = configuration.editorStore
121 let entity = Entity(name: configuration.entityType.entityName,
122 type: configuration.entityType)
123 guard let objects = try? store.objects(with: entity, sortDescriptors: sortDescriptors)
124 else { return print("Can not get entity named \(configuration.entityType.entityName)") }
126 if let object = objectSearch(objects, $0) {
127 registerElement($0, to: object)
128 } else if let new = store.insertNewObject(for: entity) {
129 registerElement($0, to: new)
131 fatalError("Can not create Entity")
135 store.saveActionCore()
139 func beginRegister(_ object: NSManagedObject) {}
140 func handleExtraValue(_ value: Any, forKey key: String, to object: NSManagedObject) -> Bool {
143 func finishOperating() {}