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