From 1e47f2009fd107b431ca25eb2ed78e82889626b5 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 17 Dec 2024 14:31:41 +0100 Subject: [PATCH] Adds documentation --- LeStorage/Services.swift | 1 + LeStorage/Store.swift | 1 + LeStorage/StoreCenter.swift | 31 ++++++++++++++++----- LeStorage/StoredCollection+Sync.swift | 31 +++++---------------- LeStorage/StoredCollection.swift | 39 +++++++++------------------ 5 files changed, 44 insertions(+), 59 deletions(-) diff --git a/LeStorage/Services.swift b/LeStorage/Services.swift index 23e93ee..8530aeb 100644 --- a/LeStorage/Services.swift +++ b/LeStorage/Services.swift @@ -542,6 +542,7 @@ public class Services { let _: Empty = try await self._runRequest(serviceCall: postDeviceTokenCall, payload: token) } + /// Returns the list of DataAccess public func getUserDataAccess() async throws { let request = try self._baseRequest(call: getUserDataAccessCall) try await self._runRequest(request) { data in diff --git a/LeStorage/Store.swift b/LeStorage/Store.swift index faf6939..d984899 100644 --- a/LeStorage/Store.swift +++ b/LeStorage/Store.swift @@ -182,6 +182,7 @@ final public class Store { } } + /// Returns the list of synchronized collection inside the store fileprivate func _syncedCollections() -> [any SomeSyncedCollection] { return self._collections.values.compactMap { $0 as? any SomeSyncedCollection } } diff --git a/LeStorage/StoreCenter.swift b/LeStorage/StoreCenter.swift index e023251..c23868e 100644 --- a/LeStorage/StoreCenter.swift +++ b/LeStorage/StoreCenter.swift @@ -409,14 +409,13 @@ public class StoreCenter { // MARK: - Synchronization + /// Creates the ApiCallCollection to manage the calls to the API fileprivate func _createSyncApiCallCollection() { self.loadApiCallCollection(type: GetSyncData.self) } + /// Loads all the data from the server for the users public func initialSynchronization() { - self._settingsStorage.update { settings in - settings.lastSynchronization = Date() - } Store.main.loadCollectionsFromServer() // request data that has been shared with the user @@ -430,6 +429,7 @@ public class StoreCenter { } + /// Basically asks the server for new content func synchronizeLastUpdates() async throws { let lastSync = self._settingsStorage.item.lastSynchronization @@ -445,6 +445,7 @@ public class StoreCenter { } + /// Processes Data Access data func userDataAccessRetrieved(_ data: Data) { do { guard @@ -463,6 +464,7 @@ public class StoreCenter { } } + /// Processes the data coming from a sync request func synchronizeContent(_ data: Data) { do { @@ -504,6 +506,10 @@ public class StoreCenter { } } + /// Processes data that should be inserted or updated inside the app + /// - Parameters: + /// - updates: the server updates + /// - shared: indicates if the content should be flagged as shared fileprivate func _parseSyncUpdates(_ updates: [String: Any], shared: Bool = false) throws { for (className, updateData) in updates { guard let updateArray = updateData as? [[String: Any]] else { @@ -530,6 +536,7 @@ public class StoreCenter { } } + /// Processes data that should be deleted inside the app fileprivate func _parseSyncDeletions(_ deletions: [String: Any]) throws { for (className, deleteData) in deletions { guard let deletedItems = deleteData as? [Any] else { @@ -552,6 +559,7 @@ public class StoreCenter { } } + /// Processes data that has been revoked fileprivate func _parseSyncRevocations(_ deletions: [String: Any], parents: [[String: Any]]?) throws { for (className, revocationData) in deletions { guard let revokedItems = revocationData as? [Any] else { @@ -590,6 +598,7 @@ public class StoreCenter { } } + /// Returns a Type object for a class name static func classFromName(_ className: String) throws -> any SyncedStorable.Type { if let type = ClassLoader.getClass(className) as? any SyncedStorable.Type { return type @@ -598,6 +607,7 @@ public class StoreCenter { } } + /// Returns the store corresponding to the provided id, and creates one if necessary fileprivate func _store(id: String?) -> Store { if let storeId = id { if let store = self._stores[storeId] { @@ -612,12 +622,14 @@ public class StoreCenter { } } + /// Returns whether a data has already been deleted by, to avoid inserting it again fileprivate func _hasAlreadyBeenDeleted(_ instance: T) -> Bool { return self._dataLogs.contains(where: { $0.dataId == instance.stringId && $0.operation == .delete }) } + /// Adds or updates an instance into the store func synchronizationAddOrUpdate(_ instance: T, storeId: String?, shared: Bool) { let hasAlreadyBeenDeleted: Bool = self._hasAlreadyBeenDeleted(instance) if !hasAlreadyBeenDeleted { @@ -627,6 +639,7 @@ public class StoreCenter { } } + /// Deletes an instance with the given parameters func synchronizationDelete(id: String, model: String, storeId: String?) { DispatchQueue.main.async { @@ -640,6 +653,7 @@ public class StoreCenter { } } + /// Revokes a data that has been shared with the user func synchronizationRevoke(id: String, model: String, storeId: String?) { DispatchQueue.main.async { @@ -657,25 +671,25 @@ public class StoreCenter { } } + /// Returns whether an instance has been shared with the user fileprivate func _instanceShared(id: String, type: T.Type) -> Bool { let realId: T.ID = T.buildRealId(id: id) let instance: T? = Store.main.findById(realId) return instance?.shared == true } + /// Deletes a data log by data id fileprivate func _cleanupDataLog(dataId: String) { let logs = self._dataLogs.filter { $0.dataId == dataId } self._dataLogs.delete(contentOfs: logs) } - // func createInsertLog(_ instance: T) { - // self._addDataLog(instance, method: .post) - // } - + /// Creates a delete log for an instance func createDeleteLog(_ instance: T) { self._addDataLog(instance, method: .delete) } + /// Adds a datalog for an instance with the associated method fileprivate func _addDataLog(_ instance: T, method: HTTPMethod) { let dataLog = DataLog( dataId: instance.stringId, modelName: String(describing: T.self), operation: method) @@ -684,6 +698,7 @@ public class StoreCenter { // MARK: - Miscellanous + /// Returns the count of api calls for a Type public func apiCallCount(type: T.Type) async -> Int { do { let collection: ApiCallCollection = try self.apiCallCollection() @@ -842,6 +857,7 @@ public class StoreCenter { // MARK: - Data Access + /// Returns the list of users have access to a data given its id public func authorizedUsers(for modelId: String) -> [String] { guard let dataAccessCollection = self._dataAccess else { return [] @@ -852,6 +868,7 @@ public class StoreCenter { return [] } + /// Sets the the list of authorized users for an instance public func setAuthorizedUsers(for instance: T, users: [String]) throws { guard let dataAccessCollection = self._dataAccess else { return diff --git a/LeStorage/StoredCollection+Sync.swift b/LeStorage/StoredCollection+Sync.swift index eaa3503..4cf840d 100644 --- a/LeStorage/StoredCollection+Sync.swift +++ b/LeStorage/StoredCollection+Sync.swift @@ -92,30 +92,8 @@ extension StoredCollection: SomeSyncedCollection where T : SyncedStorable { self.deleteItem(instance) } } - - /// Deletes the instance in the collection without synchronization -// func revokeByStringIdNoSync(_ id: String) { -// defer { -// self.setChanged() -// } -// let realId = T.buildRealId(id: id) -// if let instance = self.findById(realId) { -// self.deleteItemIfUnused(instance) -// } -// } - -// fileprivate func _buildRealId(id: String) -> T.ID? { -// switch T.ID.self { -// case is String.Type: -// return id as? T.ID -// case is Int64.Type: -// return Formatter.number.number(from: id)?.int64Value as? T.ID -// default: -// fatalError("ID \(type(of: T.ID.self)) is neither String nor Int, can't parse \(id)") -//// return nil -// } -// } - + + /// Adds or update an instance and writes public func addOrUpdate(instance: T) { defer { self.setChanged() @@ -131,6 +109,7 @@ extension StoredCollection: SomeSyncedCollection where T : SyncedStorable { } } + /// Adds or update a sequence and writes public func addOrUpdate(contentOfs sequence: any Sequence) { defer { self.setChanged() @@ -166,14 +145,15 @@ extension StoredCollection: SomeSyncedCollection where T : SyncedStorable { } } + /// Deletes an instance and writes public func delete(instance: T) { defer { self.setChanged() } - self._deleteNoWrite(instance: instance) } + /// Deletes an instance without writing, logs the operation and sends an API call fileprivate func _deleteNoWrite(instance: T) { self.deleteItem(instance) StoreCenter.main.createDeleteLog(instance) @@ -227,6 +207,7 @@ extension StoredCollection: SomeSyncedCollection where T : SyncedStorable { // MARK: - Synchronization + /// Adds or update an instance if it is newer than the local instance func addOrUpdateIfNewer(_ instance: T, shared: Bool) { defer { self.setChanged() diff --git a/LeStorage/StoredCollection.swift b/LeStorage/StoredCollection.swift index 003c098..fc6ff73 100644 --- a/LeStorage/StoredCollection.swift +++ b/LeStorage/StoredCollection.swift @@ -87,16 +87,19 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti self.store = Store.main } + /// Returns a dummy StoredCollection instance public static func placeholder() -> StoredCollection { return StoredCollection() } + /// Returns the name of the managed resource var resourceName: String { return T.resourceName() } // MARK: - Loading + /// Sets the collection as changed to trigger a write func setChanged() { self._hasChanged = true } @@ -166,6 +169,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti self.addOrUpdateItem(instance: instance) } + /// Adds or update an instance inside the collection and writes func addOrUpdateItem(instance: T) { defer { @@ -180,14 +184,6 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti } -// /// Sends a POST request for the instance, and changes the collection to perform a write -// public func writeChangeAndInsertOnServer(instance: T) { -// defer { -// self._hasChanged = true -// } -// self._sendInsertionIfNecessary(instance) -// } - /// A method the treat the collection as a single instance holder func setSingletonNoSync(instance: T) { defer { @@ -197,6 +193,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti self.addItem(instance: instance) } + /// Deletes an item by its id func deleteById(_ id: T.ID) { if let instance = self.findById(id) { self.delete(instance: instance) @@ -229,6 +226,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti // self._addOrUpdate(contentOfs: sequence) } + /// Adds a sequence of objects inside the collection and performs a write func addSequence(_ sequence: any Sequence) { defer { self._hasChanged = true @@ -244,6 +242,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti } + /// This method sets the storeId for the given instance if the collection belongs to a store with an id fileprivate func _affectStoreIdIfNecessary(instance: T) { if let storeId = self.store.identifier { if var altStorable = instance as? SideStorable { @@ -254,6 +253,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti } } + /// Adds an instance to the collection func addItem(instance: T) { self._affectStoreIdIfNecessary(instance: instance) self.items.append(instance) @@ -262,29 +262,21 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti self._applyLimitIfPresent() } + /// Updates an instance to the collection by index func updateItem(_ instance: T, index: Int) { -// var existingItem = self.items[index] -// existingItem.hasBeenDeleted() -// self._copy(instance, into: &existingItem) // we need to keep the instance alive for screen to refresh self.items[index].copy(from: instance) instance.store = self.store self._indexes?[instance.id] = instance } + /// Deletes an instance from the collection func deleteItem(_ instance: T) { instance.deleteDependencies() self.items.removeAll { $0.id == instance.id } self._indexes?.removeValue(forKey: instance.id) } - func deleteItemIfUnused(_ instance: T) { - // find if instance if referenced elsewhere - // if so, delete - instance.deleteDependencies() - self.items.removeAll { $0.id == instance.id } - self._indexes?.removeValue(forKey: instance.id) - } - + /// If the collection has more instance that its limit, remove the surplus fileprivate func _applyLimitIfPresent() { if let limit { self.items = self.items.suffix(limit) @@ -299,14 +291,6 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti return self.items.first(where: { $0.id == id }) } - /// Deletes the instance corresponding to the provided [id] -// public func deleteById(_ id: T.ID) throws { -// if let instance = self.findById(id) { -// self.deleteItem(instance) -// self._hasChanged = true -// } -// } - /// Proceeds to "hard" delete the items without synchronizing them /// Also removes related API calls public func deleteDependencies(_ items: any Sequence) { @@ -378,6 +362,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti // MARK: - Reference count + /// Counts the references to an object - given its type and id - inside the collection func referenceCount(type: S.Type, id: String) -> Int { let relationships = T.relationships().filter { $0.type == type } guard relationships.count > 0 else { return 0 }