diff --git a/LeStorage.xcodeproj/project.pbxproj b/LeStorage.xcodeproj/project.pbxproj index 8534b28..dec56c7 100644 --- a/LeStorage.xcodeproj/project.pbxproj +++ b/LeStorage.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ C4A47D532B6D2C5F00ADC637 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D522B6D2C5F00ADC637 /* Logger.swift */; }; C4A47D552B6D2DBF00ADC637 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D542B6D2DBF00ADC637 /* FileUtils.swift */; }; C4A47D612B6D3C1300ADC637 /* Services.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D602B6D3C1300ADC637 /* Services.swift */; }; + C4A47D652B6E92FE00ADC637 /* Storable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D642B6E92FE00ADC637 /* Storable.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -41,6 +42,7 @@ C4A47D522B6D2C5F00ADC637 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; C4A47D542B6D2DBF00ADC637 /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = ""; }; C4A47D602B6D3C1300ADC637 /* Services.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Services.swift; sourceTree = ""; }; + C4A47D642B6E92FE00ADC637 /* Storable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storable.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -87,6 +89,7 @@ C425D4382B6D24E1002A7B48 /* LeStorage.docc */, C4A47D602B6D3C1300ADC637 /* Services.swift */, C425D4572B6D2519002A7B48 /* Store.swift */, + C4A47D642B6E92FE00ADC637 /* Storable.swift */, C4A47D4E2B6D280200ADC637 /* StoredCollection.swift */, C4A47D582B6D352900ADC637 /* Utils */, ); @@ -225,6 +228,7 @@ C425D4392B6D24E1002A7B48 /* LeStorage.docc in Sources */, C4A47D612B6D3C1300ADC637 /* Services.swift in Sources */, C4A47D552B6D2DBF00ADC637 /* FileUtils.swift in Sources */, + C4A47D652B6E92FE00ADC637 /* Storable.swift in Sources */, C4A47D4F2B6D280200ADC637 /* StoredCollection.swift in Sources */, C425D4582B6D2519002A7B48 /* Store.swift in Sources */, ); diff --git a/LeStorage/Services.swift b/LeStorage/Services.swift index 82aecb9..d9c2d9c 100644 --- a/LeStorage/Services.swift +++ b/LeStorage/Services.swift @@ -38,6 +38,13 @@ class Services { fileprivate func runRequest(_ request: URLRequest) async throws -> T { let task: (Data, URLResponse) = try await URLSession.shared.data(for: request) + + if let response = task.1 as? HTTPURLResponse { + let statusCode = response.statusCode + Logger.log("status code = \(statusCode)") + } + + Logger.log("response = \(String(data: task.0, encoding: .utf8))") return try jsonDecoder.decode(T.self, from: task.0) } @@ -64,6 +71,7 @@ class Services { } var request = URLRequest(url: url) request.httpMethod = method.rawValue + request.setValue("application/json", forHTTPHeaderField: "Content-Type") return request } @@ -75,18 +83,21 @@ class Services { } func insert(_ instance: T) async throws -> T { - let postRequest = try postRequest(servicePath: T.resourceName + "/") + var postRequest = try postRequest(servicePath: T.resourceName + "/") + postRequest.httpBody = try instance.jsonData() return try await self.runRequest(postRequest) } func update(_ instance: T) async throws -> T { - let postRequest = try putRequest(servicePath: T.resourceName + "/") - return try await self.runRequest(postRequest) + var putRequest = try putRequest(servicePath: T.resourceName + "/") + putRequest.httpBody = try instance.jsonData() + return try await self.runRequest(putRequest) } func delete(_ instance: T) async throws -> T { - let postRequest = try deleteRequest(servicePath: T.resourceName + "/") - return try await self.runRequest(postRequest) + var deleteRequest = try deleteRequest(servicePath: T.resourceName + "/") + deleteRequest.httpBody = try instance.jsonData() + return try await self.runRequest(deleteRequest) } } diff --git a/LeStorage/Storable.swift b/LeStorage/Storable.swift new file mode 100644 index 0000000..7e43112 --- /dev/null +++ b/LeStorage/Storable.swift @@ -0,0 +1,20 @@ +// +// Storable.swift +// LeStorage +// +// Created by Laurent Morvillier on 03/02/2024. +// + +import Foundation + +public protocol Storable : Codable, Identifiable where ID : StringProtocol { + static var resourceName: String { get } +} + +extension Storable { + + public func findById(_ id: String) -> T? { + return Store.main.findById(id) + } + +} diff --git a/LeStorage/Store.swift b/LeStorage/Store.swift index c269e05..2c9fadf 100644 --- a/LeStorage/Store.swift +++ b/LeStorage/Store.swift @@ -7,28 +7,43 @@ import Foundation -protocol ServiceProvider { - var service: Services? { get } -} - -public class Store: ServiceProvider { +public class Store { - fileprivate var _synchronizationApiURL: String? - fileprivate var _services: Services? + public static let main = Store() + + public static func randomId() -> String { + return UUID().uuidString.lowercased() + } - public init(synchronizationApiURL: String? = nil) { - self._synchronizationApiURL = synchronizationApiURL - if let url = synchronizationApiURL { - self._services = Services(url: url) + public var synchronizationApiURL: String? { + didSet { + if let url = synchronizationApiURL { + self._services = Services(url: url) + } } } + fileprivate var _services: Services? + + fileprivate var collections: [String : any SomeCollection] = [:] + public init() { } + public func registerCollection(synchronized: Bool) -> StoredCollection { - return StoredCollection(synchronized: synchronized, serviceProvider: self) + let collection = StoredCollection(synchronized: synchronized, store: self) + self.collections[T.resourceName] = collection + return collection } var service: Services? { return self._services } + func findById(_ id: String) -> T? { + guard let collection = self.collections[T.resourceName] as? StoredCollection else { + Logger.w("Collection \(T.resourceName) not registered") + return nil + } + return collection.findById(id) + } + } diff --git a/LeStorage/StoredCollection.swift b/LeStorage/StoredCollection.swift index 5c377a9..23fbd76 100644 --- a/LeStorage/StoredCollection.swift +++ b/LeStorage/StoredCollection.swift @@ -7,17 +7,17 @@ import Foundation -public protocol Storable : Codable, Identifiable where ID : Hashable { - static var resourceName: String { get } +protocol SomeCollection : Identifiable { + } -public class StoredCollection : RandomAccessCollection, ObservableObject { +public class StoredCollection : RandomAccessCollection, SomeCollection, ObservableObject { let synchronized: Bool @Published public fileprivate(set) var items: [T] = [] - fileprivate var _serviceProvider: ServiceProvider + fileprivate var _store: Store fileprivate var _hasChanged: Bool = false { didSet { @@ -29,9 +29,9 @@ public class StoredCollection : RandomAccessCollection, Observable } } - init(synchronized: Bool, serviceProvider: ServiceProvider) { + init(synchronized: Bool, store: Store) { self.synchronized = synchronized - self._serviceProvider = serviceProvider + self._store = store self._load() } @@ -63,6 +63,12 @@ public class StoredCollection : RandomAccessCollection, Observable self._sendDeletionIfNecessary(instance) } + public func findById(_ id: String) -> T? { + return self.items.first(where: { $0.id == id }) + } + + // MARK: - File access + fileprivate func _scheduleWrite() { self._write() } @@ -114,13 +120,14 @@ public class StoredCollection : RandomAccessCollection, Observable // MARK: - Synchronization fileprivate func _sendInsertionIfNecessary(_ instance: T) { + Logger.log("_sendInsertionIfNecessary...") guard self.synchronized else { return } - + Logger.log("Call service...") Task { do { - let _ = try await self._serviceProvider.service?.insert(instance) + let _ = try await self._store.service?.insert(instance) } catch { Logger.error(error) } @@ -135,7 +142,7 @@ public class StoredCollection : RandomAccessCollection, Observable Task { do { - let _ = try await self._serviceProvider.service?.insert(instance) + let _ = try await self._store.service?.insert(instance) } catch { Logger.error(error) } @@ -150,7 +157,7 @@ public class StoredCollection : RandomAccessCollection, Observable Task { do { - let _ = try await self._serviceProvider.service?.delete(instance) + let _ = try await self._store.service?.delete(instance) } catch { Logger.error(error) }