Adds documentation

sync2
Laurent 11 months ago
parent 8b6770f9d2
commit 1e47f2009f
  1. 1
      LeStorage/Services.swift
  2. 1
      LeStorage/Store.swift
  3. 31
      LeStorage/StoreCenter.swift
  4. 31
      LeStorage/StoredCollection+Sync.swift
  5. 39
      LeStorage/StoredCollection.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

@ -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 }
}

@ -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<T: Storable>(_ 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<T: SyncedStorable>(_ 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<T: SyncedStorable>(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<T: Storable>(_ instance: T) {
// self._addDataLog(instance, method: .post)
// }
/// Creates a delete log for an instance
func createDeleteLog<T: Storable>(_ instance: T) {
self._addDataLog(instance, method: .delete)
}
/// Adds a datalog for an instance with the associated method
fileprivate func _addDataLog<T: Storable>(_ 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<T: SyncedStorable>(type: T.Type) async -> Int {
do {
let collection: ApiCallCollection<T> = 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<T: SyncedStorable>(for instance: T, users: [String]) throws {
guard let dataAccessCollection = self._dataAccess else {
return

@ -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<T>) {
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()

@ -87,16 +87,19 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
self.store = Store.main
}
/// Returns a dummy StoredCollection instance
public static func placeholder() -> StoredCollection<T> {
return StoredCollection<T>()
}
/// 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<T: Storable>: 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<T: Storable>: 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<T: Storable>: 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<T: Storable>: RandomAccessCollection, SomeCollecti
// self._addOrUpdate(contentOfs: sequence)
}
/// Adds a sequence of objects inside the collection and performs a write
func addSequence(_ sequence: any Sequence<T>) {
defer {
self._hasChanged = true
@ -244,6 +242,7 @@ public class StoredCollection<T: Storable>: 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<T: Storable>: 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<T: Storable>: 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<T: Storable>: 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<T>) {
@ -378,6 +362,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
// MARK: - Reference count
/// Counts the references to an object - given its type and id - inside the collection
func referenceCount<S: Storable>(type: S.Type, id: String) -> Int {
let relationships = T.relationships().filter { $0.type == type }
guard relationships.count > 0 else { return 0 }

Loading…
Cancel
Save