Adds documentation

multistore
Laurent 1 year ago
parent 59f85f998b
commit df7b1461a7
  1. 21
      LeStorage/Store.swift
  2. 16
      LeStorage/StoredCollection.swift
  3. 8
      LeStorage/StoredSingleton.swift

@ -26,8 +26,6 @@ public class Store {
/// The Store singleton /// The Store singleton
public static let main = Store() public static let main = Store()
// public fileprivate(set) var currentUser: User? = nil
/// A method to provide ids corresponding to the django storage /// A method to provide ids corresponding to the django storage
public static func randomId() -> String { public static func randomId() -> String {
return UUID().uuidString.lowercased() return UUID().uuidString.lowercased()
@ -81,16 +79,10 @@ public class Store {
let collection = StoredCollection<T>(synchronized: synchronized, store: Store.main, indexed: indexed, inMemory: inMemory, sendsUpdate: sendsUpdate, loadCompletion: nil) let collection = StoredCollection<T>(synchronized: synchronized, store: Store.main, indexed: indexed, inMemory: inMemory, sendsUpdate: sendsUpdate, loadCompletion: nil)
self._collections[T.resourceName()] = collection self._collections[T.resourceName()] = collection
// if synchronized { // register additional collection for api calls
// let apiCallCollection = StoredCollection<ApiCall<T>>(synchronized: false, store: Store.main, loadCompletion: { apiCallCollection in
// self._rescheduleCalls(collection: apiCallCollection)
// })
// self._apiCallsCollections[T.resourceName()] = apiCallCollection
// }
return collection return collection
} }
/// Registers a StoredSingleton instance
public func registerObject<T : Storable>(synchronized: Bool, inMemory: Bool = false, sendsUpdate: Bool = true) -> StoredSingleton<T> { public func registerObject<T : Storable>(synchronized: Bool, inMemory: Bool = false, sendsUpdate: Bool = true) -> StoredSingleton<T> {
// register collection // register collection
@ -102,12 +94,14 @@ public class Store {
// MARK: - Settings // MARK: - Settings
/// Stores the user UUID
func setUserUUID(uuidString: String) { func setUserUUID(uuidString: String) {
self._settingsStorage.update { settings in self._settingsStorage.update { settings in
settings.userId = uuidString settings.userId = uuidString
} }
} }
/// Returns the user's UUID
public var currentUserUUID: UUID? { public var currentUserUUID: UUID? {
if let uuidString = self._settingsStorage.item.userId, if let uuidString = self._settingsStorage.item.userId,
let uuid = UUID(uuidString: uuidString) { let uuid = UUID(uuidString: uuidString) {
@ -116,6 +110,7 @@ public class Store {
return nil return nil
} }
/// Returns a UUID, using the user's if possible
public func mandatoryUserUUID() -> UUID { public func mandatoryUserUUID() -> UUID {
if let uuid = self.currentUserUUID { if let uuid = self.currentUserUUID {
return uuid return uuid
@ -128,16 +123,19 @@ public class Store {
} }
} }
/// Returns the username
func userName() -> String? { func userName() -> String? {
return self._settingsStorage.item.username return self._settingsStorage.item.username
} }
/// Sets the username
func setUserName(_ username: String) { func setUserName(_ username: String) {
self._settingsStorage.update { settings in self._settingsStorage.update { settings in
settings.username = username settings.username = username
} }
} }
/// Disconnect the user from the storage and resets collection
public func disconnect(resetOption: ResetOption? = nil) { public func disconnect(resetOption: ResetOption? = nil) {
try? self.service().disconnect() try? self.service().disconnect()
self._settingsStorage.update { settings in self._settingsStorage.update { settings in
@ -162,6 +160,7 @@ public class Store {
} }
/// Returns whether the system has a user token
public func hasToken() -> Bool { public func hasToken() -> Bool {
do { do {
_ = try self.service().keychainStore.getToken() _ = try self.service().keychainStore.getToken()
@ -229,6 +228,7 @@ public class Store {
return try await self.service().runApiCall(apiCall) return try await self.service().runApiCall(apiCall)
} }
/// Executes an API call
func execute<T>(apiCall: ApiCall<T>) async throws -> T { func execute<T>(apiCall: ApiCall<T>) async throws -> T {
return try await self._executeApiCall(apiCall) return try await self._executeApiCall(apiCall)
} }
@ -240,12 +240,14 @@ public class Store {
return try await self.service().get() return try await self.service().get()
} }
/// Resets all registered collection
public func reset() { public func reset() {
for collection in self._collections.values { for collection in self._collections.values {
collection.reset() collection.reset()
} }
} }
/// Loads all collection with the data from the server
public func loadCollectionFromServer() { public func loadCollectionFromServer() {
for collection in self._collections.values { for collection in self._collections.values {
Task { Task {
@ -254,6 +256,7 @@ public class Store {
} }
} }
/// Returns whether any collection has pending API calls
public func hasPendingAPICalls() -> Bool { public func hasPendingAPICalls() -> Bool {
return self._collections.values.contains(where: { $0.hasPendingAPICalls() }) return self._collections.values.contains(where: { $0.hasPendingAPICalls() })
} }

@ -132,8 +132,8 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
} }
/// Starts the JSON file decoding synchronously or asynchronously
fileprivate func _loadFromFile() throws { fileprivate func _loadFromFile() throws {
// let url: URL = try self._urlForJSONFile()
if self.asynchronousIO { if self.asynchronousIO {
Task(priority: .high) { Task(priority: .high) {
@ -214,6 +214,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
} }
/// A method the treat the collection as a single instance holder
func setSingletonNoSync(instance: T) { func setSingletonNoSync(instance: T) {
defer { defer {
self._hasChanged = true self._hasChanged = true
@ -251,6 +252,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
} }
} }
/// Adds or update a sequence of elements
public func addOrUpdate(contentOfs sequence: any Sequence<T>) throws { public func addOrUpdate(contentOfs sequence: any Sequence<T>) throws {
try self._addOrUpdate(contentOfs: sequence) try self._addOrUpdate(contentOfs: sequence)
} }
@ -310,6 +312,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
} }
/// Proceeds to delete all instance of the collection, properly cleaning up dependencies and sending API calls
public func deleteAll() throws { public func deleteAll() throws {
try self.delete(contentOfs: self.items) try self.delete(contentOfs: self.items)
} }
@ -350,10 +353,12 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
Logger.log("End write") Logger.log("End write")
} }
/// Simply clears the items of the collection
func clear() { func clear() {
self.items.removeAll() self.items.removeAll()
} }
/// Removes the items of the collection, deletes the corresponding file, and also reset the related API calls collection
public func reset() { public func reset() {
self.items.removeAll() self.items.removeAll()
@ -373,6 +378,8 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
// MARK: - Synchronization // MARK: - Synchronization
/// Returns an APICall instance for the Storable [instance] and an HTTP [method]
/// The method updates existing calls or creates a new one
fileprivate func _callForInstance(_ instance: T, method: HTTPMethod) throws -> ApiCall<T>? { fileprivate func _callForInstance(_ instance: T, method: HTTPMethod) throws -> ApiCall<T>? {
guard let apiCallCollection = self.apiCallsCollection else { guard let apiCallCollection = self.apiCallsCollection else {
throw StoredCollectionError.missingApiCallCollection throw StoredCollectionError.missingApiCallCollection
@ -395,6 +402,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
} }
} }
/// Creates an API call for the Storable [instance] and an HTTP [method]
fileprivate func _createCall(_ instance: T, method: HTTPMethod) throws -> ApiCall<T> { fileprivate func _createCall(_ instance: T, method: HTTPMethod) throws -> ApiCall<T> {
let baseURL = try _store.service().baseURL let baseURL = try _store.service().baseURL
let jsonString = try instance.jsonString() let jsonString = try instance.jsonString()
@ -410,6 +418,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
return ApiCall(url: url, method: method.rawValue, dataId: String(instance.id), body: jsonString) return ApiCall(url: url, method: method.rawValue, dataId: String(instance.id), body: jsonString)
} }
/// Prepares a call for execution by updating its properties and adding it to its collection for storage
fileprivate func _prepareCall(apiCall: ApiCall<T>) throws { fileprivate func _prepareCall(apiCall: ApiCall<T>) throws {
apiCall.lastAttemptDate = Date() apiCall.lastAttemptDate = Date()
apiCall.attemptsCount += 1 apiCall.attemptsCount += 1
@ -488,14 +497,15 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
/// Indicates if the collection is currently retrying ApiCalls /// Indicates if the collection is currently retrying ApiCalls
fileprivate var _isRetryingCalls: Bool = false fileprivate var _isRetryingCalls: Bool = false
/// Reschedule API calls if necessary
func rescheduleApiCallsIfNecessary() { func rescheduleApiCallsIfNecessary() {
if !self._isRetryingCalls { if !self._isRetryingCalls {
self._rescheduleApiCalls() self._rescheduleApiCalls()
} }
} }
/// Reschedule API calls
fileprivate func _rescheduleApiCalls() { fileprivate func _rescheduleApiCalls() {
// return
guard let apiCallsCollection, apiCallsCollection.isNotEmpty else { guard let apiCallsCollection, apiCallsCollection.isNotEmpty else {
return return
@ -532,6 +542,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
} }
/// Deletes an API call by [id]
func deleteApiCallById(_ id: String) throws { func deleteApiCallById(_ id: String) throws {
guard let apiCallsCollection else { guard let apiCallsCollection else {
throw StoreError.apiCallCollectionNotRegistered(type: T.resourceName()) throw StoreError.apiCallCollectionNotRegistered(type: T.resourceName())
@ -539,6 +550,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
try apiCallsCollection.deleteById(id) try apiCallsCollection.deleteById(id)
} }
/// Returns if the API call collection is not empty
func hasPendingAPICalls() -> Bool { func hasPendingAPICalls() -> Bool {
guard let apiCallsCollection else { return false } guard let apiCallsCollection else { return false }
return apiCallsCollection.isNotEmpty return apiCallsCollection.isNotEmpty

@ -7,28 +7,28 @@
import Foundation import Foundation
/// A class extending the capabilities of StoredCollection but supposedly manages only one item
public class StoredSingleton<T: Storable>: StoredCollection<T> { public class StoredSingleton<T: Storable>: StoredCollection<T> {
/// Sets the singleton to the collection without synchronizing it
public func setItemNoSync(_ instance: T) { public func setItemNoSync(_ instance: T) {
self.setSingletonNoSync(instance: instance) self.setSingletonNoSync(instance: instance)
} }
/// updates the existing singleton
public func update() throws { public func update() throws {
if let item = self.item() { if let item = self.item() {
try self.addOrUpdate(instance: item) try self.addOrUpdate(instance: item)
} }
} }
/// Returns the singleton
public func item() -> T? { public func item() -> T? {
return self.items.first return self.items.first
} }
// MARK: - Protects from use // MARK: - Protects from use
// public override func addOrUpdate(instance: T) throws {
// fatalError("method unavailable for StoredSingleton")
// }
public override func addOrUpdate(contentOfs sequence: any Sequence<T>) throws { public override func addOrUpdate(contentOfs sequence: any Sequence<T>) throws {
fatalError("method unavailable for StoredSingleton, use update") fatalError("method unavailable for StoredSingleton, use update")
} }

Loading…
Cancel
Save