parent
56a2f6e618
commit
f926a1fcbe
@ -0,0 +1,25 @@ |
||||
// |
||||
// SyncData.swift |
||||
// LeStorage |
||||
// |
||||
// Created by Laurent Morvillier on 18/10/2024. |
||||
// |
||||
|
||||
import Foundation |
||||
|
||||
class GetSyncData: ModelObject, SyncedStorable { |
||||
|
||||
static func filterByStoreIdentifier() -> Bool { return false } |
||||
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } |
||||
var lastUpdate: Date = Date() |
||||
|
||||
static func resourceName() -> String { |
||||
return "data" |
||||
} |
||||
|
||||
func copy(from other: any Storable) { |
||||
guard let getSyncData = other as? GetSyncData else { return } |
||||
self.lastUpdate = getSyncData.lastUpdate |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,126 @@ |
||||
// |
||||
// 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")) |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue