import Cocoa
import SwiftyJSON
+import Doutaku
-struct MappingConfiguration<T: NSManagedObject> {
- let entity: Entity<T>
+struct MappingConfiguration<T: Entity> {
+
+ let entity: T.Type
let dataKeys: [String]
- let primaryKey: String
- let compositPrimaryKeys: [String]?
+ let primaryKeys: [String]
let editorStore: CoreDataAccessor
let ignoreKeys: [String]
- init(entity: Entity<T>,
+ init(entity: T.Type,
dataKeys: [String] = ["api_data"],
- primaryKey: String = "id",
- compositPrimaryKeys: [String]? = nil,
+ primaryKeys: [String] = ["id"],
editorStore: CoreDataAccessor,
ignoreKeys: [String] = []) {
+
self.entity = entity
self.dataKeys = dataKeys
- self.primaryKey = primaryKey
- self.compositPrimaryKeys = compositPrimaryKeys
+ self.primaryKeys = primaryKeys
self.editorStore = editorStore
self.ignoreKeys = ignoreKeys
}
}
protocol JSONMapper {
- associatedtype ObjectType: NSManagedObject
+
+ associatedtype ObjectType: Entity
init(_ apiResponse: APIResponse)
}
extension String {
+ // delete api_ prefix.
func keyByDeletingPrefix() -> String {
- if self.characters.count < 5 { return self }
- let s = self.index(self.startIndex, offsetBy: 4)
- return self[s..<self.endIndex]
+
+ if self.count < 5 {
+
+ return self
+ }
+
+ return String(self[index(startIndex, offsetBy: 4)...])
}
}
extension JSONMapper {
+
var data: JSON { return apiResponse.json[configuration.dataKeys] }
private func isEqual(_ lhs: AnyObject?, _ rhs: AnyObject?) -> Bool {
- if lhs == nil, rhs == nil { return true }
- if let lhs = lhs, let rhs = rhs { return lhs.isEqual(rhs) }
+
+ if lhs == nil, rhs == nil {
+
+ return true
+ }
+ if let lhs = lhs, let rhs = rhs {
+
+ return lhs.isEqual(rhs)
+ }
+
return false
}
+
func setValueIfNeeded(_ value: JSON, to object: ObjectType, forKey key: String) {
+
var validValue = value.object as AnyObject?
do {
+
try object.validateValue(&validValue, forKey: key)
+
} catch {
+
return
}
let old = object.value(forKey: key)
if !isEqual(old as AnyObject?, validValue) {
- object.willChangeValue(forKey: key)
+
object.setValue(validValue, forKey: key)
- object.didChangeValue(forKey: key)
}
}
func registerElement(_ element: JSON, to object: ObjectType) {
+
beginRegister(object)
- element.forEach { (key: String, value: JSON) in
- if configuration.ignoreKeys.contains(key) { return }
- if handleExtraValue(value, forKey: key, to: object) { return }
+ element.forEach { (key, value) in
+
+ if configuration.ignoreKeys.contains(key) {
+
+ return
+ }
+ if handleExtraValue(value, forKey: key, to: object) {
+
+ return
+ }
+
switch value.type {
case .array:
value.array?.enumerated().forEach {
+
let newKey = "\(key)_\($0.offset)"
setValueIfNeeded($0.element, to: object, forKey: newKey)
}
+
case .dictionary:
value.forEach { (subKey: String, subValue) in
+
let newKey = "\(key)_D_\(subKey.keyByDeletingPrefix())"
setValueIfNeeded(subValue, to: object, forKey: newKey)
}
+
default:
setValueIfNeeded(value, to: object, forKey: key)
+
}
}
}
+
private var sortDescriptors: [NSSortDescriptor] {
- let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey]
- return keys.map { NSSortDescriptor(key: $0, ascending: true) }
+
+ return configuration.primaryKeys.map { NSSortDescriptor(key: $0, ascending: true) }
}
+
private func objectSearch(_ objects: [ObjectType], _ element: JSON) -> ObjectType? {
- let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey]
- let keyPiar = keys.map { (key: $0, apiKey: "api_\($0)") }
+
+ let keyPiar = configuration.primaryKeys.map { (key: $0, apiKey: "api_\($0)") }
+
return objects.binarySearch {
+
+ // TODO: replace to forEach
for piar in keyPiar {
- guard let v1 = $0.value(forKey: piar.key)
- else { return .orderedAscending }
- if element[piar.apiKey].type == .null { return .orderedDescending }
+
+ guard let v1 = $0.value(forKey: piar.key) else {
+
+ return .orderedAscending
+ }
+
+ if element[piar.apiKey].type == .null {
+
+ return .orderedDescending
+ }
+
let v2 = element[piar.apiKey].object
+
return (v1 as AnyObject).compare(v2)
}
+
return .orderedDescending
}
}
- func commit() {
+
+ private func sortedObjects<ResultType: Entity>(_ entity: ResultType.Type) -> [ResultType] {
+
+ let store = configuration.editorStore
+
+ guard let objects = try? store.objects(of: configuration.entity) else {
+
+ Logger.shared.log("Can not get entity named \(configuration.entity)")
+
+ return []
+ }
+
+ return (objects as NSArray).sortedArray(using: sortDescriptors) as? [ResultType] ?? []
+ }
+ private func commintInContext() {
+
let store = configuration.editorStore
- guard let objects = try? store.objects(with: configuration.entity, sortDescriptors: sortDescriptors)
- else { return print("Can not get entity named \(configuration.entity.name)") }
+ let objects = sortedObjects(configuration.entity)
+
let list = (data.type == .array ? data.arrayValue : [data])
list.forEach {
+
if let object = objectSearch(objects, $0) {
+
registerElement($0, to: object)
+
} else if let new = store.insertNewObject(for: configuration.entity) {
+
registerElement($0, to: new)
+
} else {
- fatalError("Can not get entity named \(configuration.entity.name)")
+
+ fatalError("Can not get entity named \(configuration.entity)")
}
}
+
finishOperating()
- store.saveActionCore()
+ store.save()
+ }
+
+ func commit() {
+
+ configuration.editorStore
+ .sync {
+
+ self.commintInContext()
+ }
}
func beginRegister(_ object: ObjectType) {}
+
func handleExtraValue(_ value: JSON, forKey key: String, to object: ObjectType) -> Bool {
+
return false
}
+
func finishOperating() {}
}