|
|
|
|
@ -51,19 +51,18 @@ public class Store { |
|
|
|
|
/// The dictionary of registered StoredCollections |
|
|
|
|
fileprivate var _collections: [String : any SomeCollection] = [:] |
|
|
|
|
|
|
|
|
|
// /// The dictionary of ApiCall StoredCollections corresponding to the synchronized registered collections |
|
|
|
|
// fileprivate var _apiCallsCollections: [String : any SomeCollection] = [:] |
|
|
|
|
|
|
|
|
|
/// The list of migrations to apply |
|
|
|
|
fileprivate var _migrations: [SomeMigration] = [] |
|
|
|
|
|
|
|
|
|
/// The collection of performed migration on the store |
|
|
|
|
fileprivate lazy var _migrationCollection: StoredCollection<MigrationHistory> = { StoredCollection(synchronized: false, store: Store.main, asynchronousIO: false) |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
fileprivate var settingsStorage: MicroStorage<Settings> = MicroStorage() |
|
|
|
|
/// A store for the Settings object |
|
|
|
|
fileprivate var _settingsStorage: MicroStorage<Settings> = MicroStorage() |
|
|
|
|
|
|
|
|
|
/// The name of the directory to store the json files |
|
|
|
|
static let storageDirectory = "storage" |
|
|
|
|
|
|
|
|
|
/// Indicates to Stored Collection if they can synchronize |
|
|
|
|
public var collectionsCanSynchronize: Bool = true { |
|
|
|
|
didSet { |
|
|
|
|
Logger.log(">>> collectionsCanSynchronize = \(self.collectionsCanSynchronize)") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public init() { |
|
|
|
|
FileManager.default.createDirectoryInDocuments(directoryName: Store.storageDirectory) |
|
|
|
|
@ -87,10 +86,10 @@ public class Store { |
|
|
|
|
return collection |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public func registerObject<T : Storable>(synchronized: Bool, inMemory: Bool = false, sendsUpdate: Bool = true) -> StoredObject<T> { |
|
|
|
|
public func registerObject<T : Storable>(synchronized: Bool, inMemory: Bool = false, sendsUpdate: Bool = true) -> StoredSingleton<T> { |
|
|
|
|
|
|
|
|
|
// register collection |
|
|
|
|
let storedObject = StoredObject<T>(synchronized: synchronized, store: Store.main, inMemory: inMemory, sendsUpdate: sendsUpdate, loadCompletion: nil) |
|
|
|
|
let storedObject = StoredSingleton<T>(synchronized: synchronized, store: Store.main, inMemory: inMemory, sendsUpdate: sendsUpdate, loadCompletion: nil) |
|
|
|
|
self._collections[T.resourceName()] = storedObject |
|
|
|
|
|
|
|
|
|
return storedObject |
|
|
|
|
@ -99,18 +98,25 @@ public class Store { |
|
|
|
|
// MARK: - Settings |
|
|
|
|
|
|
|
|
|
func setUserUUID(uuidString: String) { |
|
|
|
|
self.settingsStorage.update { settings in |
|
|
|
|
self._settingsStorage.update { settings in |
|
|
|
|
settings.userId = uuidString |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public func currentUserUUID() -> UUID { |
|
|
|
|
if let uuidString = self.settingsStorage.item.userId, |
|
|
|
|
let uuid = UUID(uuidString: uuidString) { |
|
|
|
|
public var currentUserUUID: UUID? { |
|
|
|
|
if let uuidString = self._settingsStorage.item.userId, |
|
|
|
|
let uuid = UUID(uuidString: uuidString) { |
|
|
|
|
return uuid |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public func mandatoryUserUUID() -> UUID { |
|
|
|
|
if let uuid = self.currentUserUUID { |
|
|
|
|
return uuid |
|
|
|
|
} else { |
|
|
|
|
let uuid = UIDevice.current.identifierForVendor ?? UUID() |
|
|
|
|
self.settingsStorage.update { settings in |
|
|
|
|
self._settingsStorage.update { settings in |
|
|
|
|
settings.userId = uuid.uuidString |
|
|
|
|
} |
|
|
|
|
return uuid |
|
|
|
|
@ -118,18 +124,18 @@ public class Store { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func userName() -> String? { |
|
|
|
|
return self.settingsStorage.item.username |
|
|
|
|
return self._settingsStorage.item.username |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func setUserName(_ username: String) { |
|
|
|
|
self.settingsStorage.update { settings in |
|
|
|
|
self._settingsStorage.update { settings in |
|
|
|
|
settings.username = username |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public func disconnect(resetAll: Bool = false) { |
|
|
|
|
try? self.service().disconnect() |
|
|
|
|
self.settingsStorage.update { settings in |
|
|
|
|
self._settingsStorage.update { settings in |
|
|
|
|
settings.username = nil |
|
|
|
|
settings.userId = nil |
|
|
|
|
} |
|
|
|
|
@ -182,47 +188,7 @@ public class Store { |
|
|
|
|
public func deleteDependencies<T: Storable>(items: any Sequence<T>) throws { |
|
|
|
|
try self.collection().deleteDependencies(items) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// MARK: - Migration |
|
|
|
|
|
|
|
|
|
/// [beta] Adds a migration to perform |
|
|
|
|
public func addMigration(_ migration: SomeMigration) { |
|
|
|
|
self._migrations.append(migration) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// [beta] Performs the migration if necessary |
|
|
|
|
func performMigrationIfNecessary<T: Storable>(_ collection: StoredCollection<T>) async throws { |
|
|
|
|
|
|
|
|
|
// Check for migrations |
|
|
|
|
let migrations = self._migrations.filter { $0.resourceName == T.resourceName() } |
|
|
|
|
if migrations.isEmpty { return } |
|
|
|
|
|
|
|
|
|
// Check for applied migrations |
|
|
|
|
var version: Int = -1 |
|
|
|
|
var performedMigration: MigrationHistory? = self._migrationCollection.first(where: { $0.resourceName == T.resourceName() }) |
|
|
|
|
if let performedMigration { |
|
|
|
|
version = Int(performedMigration.version) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Apply necessary migrations |
|
|
|
|
let applicableMigrations = migrations.filter { $0.version > version } |
|
|
|
|
.sorted(keyPath: \.version) |
|
|
|
|
for migration in applicableMigrations { |
|
|
|
|
|
|
|
|
|
Logger.log("Start migration for \(migration.resourceName), version: \(migration.version)") |
|
|
|
|
|
|
|
|
|
try migration.migrate(synchronized: collection.synchronized) |
|
|
|
|
|
|
|
|
|
if let performedMigration { |
|
|
|
|
performedMigration.version = migration.version |
|
|
|
|
} else { |
|
|
|
|
performedMigration = MigrationHistory(version: migration.version, resourceName: migration.resourceName) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try self._migrationCollection.addOrUpdate(instance: performedMigration!) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// MARK: - Api call rescheduling |
|
|
|
|
|
|
|
|
|
/// Deletes an ApiCall by [id] and [collectionName] |
|
|
|
|
@ -236,6 +202,9 @@ public class Store { |
|
|
|
|
|
|
|
|
|
/// Reschedule an ApiCall by id |
|
|
|
|
func rescheduleApiCall<T: Storable>(id: String, type: T.Type) throws { |
|
|
|
|
guard self.collectionsCanSynchronize else { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
let collection: StoredCollection<T> = try self.collection() |
|
|
|
|
collection.rescheduleApiCallsIfNecessary() |
|
|
|
|
} |
|
|
|
|
@ -245,11 +214,6 @@ public class Store { |
|
|
|
|
return try await self.service().runApiCall(apiCall) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Executes an ApiCall |
|
|
|
|
// func execute<T>(apiCall: ApiCall<T>) async throws { |
|
|
|
|
// _ = try await self._executeApiCall(apiCall) |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
func execute<T>(apiCall: ApiCall<T>) async throws -> T { |
|
|
|
|
return try await self._executeApiCall(apiCall) |
|
|
|
|
} |
|
|
|
|
@ -265,9 +229,9 @@ public class Store { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public func loadCollections() { |
|
|
|
|
public func loadCollectionFromServer() { |
|
|
|
|
for collection in self._collections.values { |
|
|
|
|
try? collection.loadDataFromServer() |
|
|
|
|
try? collection.loadDataFromServerIfAllowed() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|