parent
2cf2bf9c61
commit
c3c9718cb2
@ -0,0 +1,36 @@ |
|||||||
|
// |
||||||
|
// DataAcces.swift |
||||||
|
// LeStorage |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 21/11/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
|
||||||
|
class DataAccess: ModelObject, SyncedStorable { |
||||||
|
var lastUpdate: Date |
||||||
|
|
||||||
|
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } |
||||||
|
static func resourceName() -> String { return "data-access" } |
||||||
|
static func filterByStoreIdentifier() -> Bool { return false } |
||||||
|
|
||||||
|
func copy(from other: any Storable) { |
||||||
|
guard let dataAccess = other as? DataAccess else { return } |
||||||
|
self.id = dataAccess.id |
||||||
|
self.owner = dataAccess.owner |
||||||
|
self.sharedWith = dataAccess.sharedWith |
||||||
|
self.modelName = dataAccess.modelName |
||||||
|
self.modelId = dataAccess.modelId |
||||||
|
self.grantedAt = dataAccess.grantedAt |
||||||
|
// self.lastHierarchyUpdate = dataAccess.lastHierarchyUpdate |
||||||
|
} |
||||||
|
|
||||||
|
var id: String |
||||||
|
var owner: String |
||||||
|
var sharedWith: [String] |
||||||
|
var modelName: String |
||||||
|
var modelId: String |
||||||
|
var grantedAt: Date |
||||||
|
// var lastHierarchyUpdate: Date |
||||||
|
|
||||||
|
} |
||||||
@ -1,126 +0,0 @@ |
|||||||
// |
|
||||||
// SyncResponse.swift |
|
||||||
// LeStorage |
|
||||||
// |
|
||||||
// Created by Laurent Morvillier on 18/10/2024. |
|
||||||
// |
|
||||||
|
|
||||||
import Foundation |
|
||||||
|
|
||||||
struct SyncResponse: Codable { |
|
||||||
let updates: [String: [Codable]] |
|
||||||
let deletions: [String: [Int]] |
|
||||||
let date: String? |
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey { |
|
||||||
case updates, deletions, date |
|
||||||
} |
|
||||||
|
|
||||||
init(from decoder: Decoder) throws { |
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self) |
|
||||||
deletions = try container.decode([String: [Int]].self, forKey: .deletions) |
|
||||||
date = try container.decodeIfPresent(String.self, forKey: .date) |
|
||||||
|
|
||||||
let updatesContainer = try container.nestedContainer( |
|
||||||
keyedBy: DynamicCodingKeys.self, forKey: .updates) |
|
||||||
var updatesDict = [String: [AnyCodable]]() |
|
||||||
|
|
||||||
for key in updatesContainer.allKeys { |
|
||||||
// let swiftClass = try SyncResponse._classFromClassName(key.stringValue) |
|
||||||
let decodedArray = try updatesContainer.decode([AnyCodable].self, forKey: key) |
|
||||||
let typedArray = decodedArray.compactMap { $0.value as? AnyCodable } |
|
||||||
updatesDict[key.stringValue] = typedArray |
|
||||||
} |
|
||||||
updates = updatesDict |
|
||||||
} |
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws { |
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self) |
|
||||||
try container.encode(deletions, forKey: .deletions) |
|
||||||
try container.encodeIfPresent(date, forKey: .date) |
|
||||||
|
|
||||||
var updatesContainer = container.nestedContainer( |
|
||||||
keyedBy: DynamicCodingKeys.self, forKey: .updates) |
|
||||||
for (key, value) in updates { |
|
||||||
let encodableArray = value.map { AnyCodable($0) } |
|
||||||
try updatesContainer.encode( |
|
||||||
encodableArray, forKey: DynamicCodingKeys(stringValue: key)!) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
struct DynamicCodingKeys: CodingKey { |
|
||||||
var stringValue: String |
|
||||||
init?(stringValue: String) { |
|
||||||
self.stringValue = stringValue |
|
||||||
} |
|
||||||
var intValue: Int? |
|
||||||
init?(intValue: Int) { |
|
||||||
return nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// fileprivate static func _classFromClassName(_ className: String) throws -> Codable.Type { |
|
||||||
// |
|
||||||
// let fullClassName = "PadelClub.\(className)" |
|
||||||
// let modelClass: AnyClass? = NSClassFromString(fullClassName) |
|
||||||
// if let type = modelClass as? Codable.Type { |
|
||||||
// return type |
|
||||||
// } else { |
|
||||||
// throw LeStorageError.cantFindClassFromName(name: className) |
|
||||||
// } |
|
||||||
// |
|
||||||
// } |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
struct AnyCodable: Codable { |
|
||||||
let value: Any |
|
||||||
|
|
||||||
init(_ value: Any) { |
|
||||||
self.value = value |
|
||||||
} |
|
||||||
|
|
||||||
init(from decoder: Decoder) throws { |
|
||||||
let container = try decoder.singleValueContainer() |
|
||||||
if let intValue = try? container.decode(Int.self) { |
|
||||||
value = intValue |
|
||||||
} else if let doubleValue = try? container.decode(Double.self) { |
|
||||||
value = doubleValue |
|
||||||
} else if let boolValue = try? container.decode(Bool.self) { |
|
||||||
value = boolValue |
|
||||||
} else if let stringValue = try? container.decode(String.self) { |
|
||||||
value = stringValue |
|
||||||
} else if let arrayValue = try? container.decode([AnyCodable].self) { |
|
||||||
value = arrayValue.map { $0.value } |
|
||||||
} else if let dictionaryValue = try? container.decode([String: AnyCodable].self) { |
|
||||||
value = dictionaryValue.mapValues { $0.value } |
|
||||||
} else { |
|
||||||
throw DecodingError.dataCorruptedError( |
|
||||||
in: container, debugDescription: "AnyCodable value cannot be decoded") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws { |
|
||||||
var container = encoder.singleValueContainer() |
|
||||||
switch value { |
|
||||||
case let intValue as Int: |
|
||||||
try container.encode(intValue) |
|
||||||
case let doubleValue as Double: |
|
||||||
try container.encode(doubleValue) |
|
||||||
case let boolValue as Bool: |
|
||||||
try container.encode(boolValue) |
|
||||||
case let stringValue as String: |
|
||||||
try container.encode(stringValue) |
|
||||||
case let arrayValue as [Any]: |
|
||||||
try container.encode(arrayValue.map { AnyCodable($0) }) |
|
||||||
case let dictionaryValue as [String: Any]: |
|
||||||
try container.encode(dictionaryValue.mapValues { AnyCodable($0) }) |
|
||||||
default: |
|
||||||
throw EncodingError.invalidValue( |
|
||||||
value, |
|
||||||
EncodingError.Context( |
|
||||||
codingPath: container.codingPath, |
|
||||||
debugDescription: "AnyCodable value cannot be encoded")) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,41 @@ |
|||||||
|
// |
||||||
|
// ClassLoader.swift |
||||||
|
// LeStorage |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 22/11/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
|
||||||
|
class ClassLoader { |
||||||
|
static var classCache: [String: AnyClass] = [:] |
||||||
|
|
||||||
|
static func getClass(_ className: String) -> AnyClass? { |
||||||
|
if let cachedClass = classCache[className] { |
||||||
|
return cachedClass |
||||||
|
} |
||||||
|
|
||||||
|
if let projectName = Bundle.main.infoDictionary?["CFBundleName"] as? String { |
||||||
|
let fullName = "\(projectName).\(className)" |
||||||
|
if let projectClass = _getClass(fullName) { |
||||||
|
return projectClass |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let fullName = "LeStorage.\(className)" |
||||||
|
if let projectClass = _getClass(fullName) { |
||||||
|
return projectClass |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
static func _getClass(_ className: String) -> AnyClass? { |
||||||
|
if let loadedClass = NSClassFromString(className) { |
||||||
|
classCache[className] = loadedClass |
||||||
|
return loadedClass |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue