|
|
|
|
@ -87,26 +87,78 @@ public class SyncedCollection<T : SyncedStorable>: BaseCollection<T>, SomeSynced |
|
|
|
|
|
|
|
|
|
// MARK: - Basic operations with sync |
|
|
|
|
|
|
|
|
|
/// Adds or update an instance and writes |
|
|
|
|
/// Adds or update an instance asynchronously and waits for network operations |
|
|
|
|
func addOrUpdateAsync(instance: T) async { |
|
|
|
|
if let result = _addOrUpdateCore(instance: instance) { |
|
|
|
|
if result.isNewItem { |
|
|
|
|
await self._executeBatchOnce(OperationBatch(insert: result.item)) |
|
|
|
|
} else { |
|
|
|
|
await self._executeBatchOnce(OperationBatch(update: result.item)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Adds or update an instance synchronously, dispatching network operations to background tasks |
|
|
|
|
public override func addOrUpdate(instance: T) { |
|
|
|
|
|
|
|
|
|
if let result = _addOrUpdateCore(instance: instance) { |
|
|
|
|
if result.isNewItem { |
|
|
|
|
Task { await self._sendInsertion(result.item) } |
|
|
|
|
} else { |
|
|
|
|
Task { await self._sendUpdate(result.item) } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Private helper function that contains the shared logic |
|
|
|
|
private func _addOrUpdateCore(instance: T) -> (item: T, isNewItem: Bool)? { |
|
|
|
|
instance.lastUpdate = Date() |
|
|
|
|
if let index = self.items.firstIndex(where: { $0.id == instance.id }) { |
|
|
|
|
if self.updateItem(instance, index: index, shouldBeSynchronized: true) { |
|
|
|
|
self._sendUpdate(instance) |
|
|
|
|
self.setChanged() |
|
|
|
|
return (instance, false) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if self.addItem(instance: instance, shouldBeSynchronized: true) { |
|
|
|
|
self._sendInsertion(instance) |
|
|
|
|
self.setChanged() |
|
|
|
|
return (instance, true) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Adds or update a sequence and writes |
|
|
|
|
override public func addOrUpdate(contentOfs sequence: any Sequence<T>) { |
|
|
|
|
// Logger.log("\(T.resourceName()) : \(sequence.underestimatedCount) items") |
|
|
|
|
// func addOrUpdateAsync(instance: T) async { |
|
|
|
|
// instance.lastUpdate = Date() |
|
|
|
|
// if let index = self.items.firstIndex(where: { $0.id == instance.id }) { |
|
|
|
|
// if self.updateItem(instance, index: index, shouldBeSynchronized: true) { |
|
|
|
|
// await self._sendUpdate(instance) |
|
|
|
|
// self.setChanged() |
|
|
|
|
// } |
|
|
|
|
// } else { |
|
|
|
|
// if self.addItem(instance: instance, shouldBeSynchronized: true) { |
|
|
|
|
// await self._sendInsertion(instance) |
|
|
|
|
// self.setChanged() |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
// |
|
|
|
|
// /// Adds or update an instance and writes |
|
|
|
|
// public override func addOrUpdate(instance: T) { |
|
|
|
|
// instance.lastUpdate = Date() |
|
|
|
|
// if let index = self.items.firstIndex(where: { $0.id == instance.id }) { |
|
|
|
|
// if self.updateItem(instance, index: index, shouldBeSynchronized: true) { |
|
|
|
|
// Task { await self._sendUpdate(instance) } |
|
|
|
|
// self.setChanged() |
|
|
|
|
// } |
|
|
|
|
// } else { |
|
|
|
|
// if self.addItem(instance: instance, shouldBeSynchronized: true) { |
|
|
|
|
// Task { await self._sendInsertion(instance) } |
|
|
|
|
// self.setChanged() |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
fileprivate func _addOrUpdateCore(contentOfs sequence: any Sequence<T>) -> OperationBatch<T> { |
|
|
|
|
|
|
|
|
|
defer { |
|
|
|
|
self.setChanged() |
|
|
|
|
} |
|
|
|
|
@ -126,9 +178,19 @@ public class SyncedCollection<T : SyncedStorable>: BaseCollection<T>, SomeSynced |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return batch |
|
|
|
|
|
|
|
|
|
self._sendOperationBatch(batch) |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Adds or update a sequence and writes |
|
|
|
|
override public func addOrUpdate(contentOfs sequence: any Sequence<T>) { |
|
|
|
|
let batch = self._addOrUpdateCore(contentOfs: sequence) |
|
|
|
|
Task { await self._sendOperationBatch(batch) } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func addOrUpdateAsync(contentOfs sequence: any Sequence<T>) async { |
|
|
|
|
let batch = self._addOrUpdateCore(contentOfs: sequence) |
|
|
|
|
await self._executeBatchOnce(batch) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Proceeds to delete all instance of the collection, properly cleaning up dependencies and sending API calls |
|
|
|
|
@ -137,13 +199,11 @@ public class SyncedCollection<T : SyncedStorable>: BaseCollection<T>, SomeSynced |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Deletes all items of the sequence by id and sets the collection as changed to trigger a write |
|
|
|
|
public override func delete(contentOfs sequence: any RandomAccessCollection<T>) { |
|
|
|
|
fileprivate func _deleteCore(contentOfs sequence: any RandomAccessCollection<T>) -> OperationBatch<T> { |
|
|
|
|
|
|
|
|
|
defer { |
|
|
|
|
self.setChanged() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
guard sequence.isNotEmpty else { return } |
|
|
|
|
|
|
|
|
|
var deleted: [T] = [] |
|
|
|
|
|
|
|
|
|
@ -156,7 +216,30 @@ public class SyncedCollection<T : SyncedStorable>: BaseCollection<T>, SomeSynced |
|
|
|
|
|
|
|
|
|
let batch = OperationBatch<T>() |
|
|
|
|
batch.deletes = deleted |
|
|
|
|
self._sendOperationBatch(batch) |
|
|
|
|
return batch |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Deletes all items of the sequence by id and sets the collection as changed to trigger a write |
|
|
|
|
public override func delete(contentOfs sequence: any RandomAccessCollection<T>) { |
|
|
|
|
guard sequence.isNotEmpty else { return } |
|
|
|
|
let batch = self._deleteCore(contentOfs: sequence) |
|
|
|
|
Task { await self._sendOperationBatch(batch) } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Deletes all items of the sequence by id and sets the collection as changed to trigger a write |
|
|
|
|
public func deleteAsync(contentOfs sequence: any RandomAccessCollection<T>) async { |
|
|
|
|
guard sequence.isNotEmpty else { return } |
|
|
|
|
let batch = self._deleteCore(contentOfs: sequence) |
|
|
|
|
await self._executeBatchOnce(batch) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Deletes an instance and writes |
|
|
|
|
func deleteAsync(instance: T) async { |
|
|
|
|
defer { |
|
|
|
|
self.setChanged() |
|
|
|
|
} |
|
|
|
|
self._deleteNoWrite(instance: instance) |
|
|
|
|
Task { await self._executeBatchOnce(OperationBatch(delete: instance)) } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Deletes an instance and writes |
|
|
|
|
@ -165,14 +248,14 @@ public class SyncedCollection<T : SyncedStorable>: BaseCollection<T>, SomeSynced |
|
|
|
|
self.setChanged() |
|
|
|
|
} |
|
|
|
|
self._deleteNoWrite(instance: instance) |
|
|
|
|
Task { await self._sendDeletion(instance) } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Deletes an instance without writing, logs the operation and sends an API call |
|
|
|
|
fileprivate func _deleteNoWrite(instance: T) { |
|
|
|
|
self.deleteItem(instance, shouldBeSynchronized: true) |
|
|
|
|
StoreCenter.main.createDeleteLog(instance) |
|
|
|
|
|
|
|
|
|
self._sendDeletion(instance) |
|
|
|
|
// await self._sendDeletion(instance) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public func deleteDependencies(_ items: any RandomAccessCollection<T>, shouldBeSynchronized: Bool) { |
|
|
|
|
@ -227,25 +310,31 @@ public class SyncedCollection<T : SyncedStorable>: BaseCollection<T>, SomeSynced |
|
|
|
|
|
|
|
|
|
// MARK: - Send requests |
|
|
|
|
|
|
|
|
|
fileprivate func _sendInsertion(_ instance: T) { |
|
|
|
|
self._sendOperationBatch(OperationBatch(insert: instance)) |
|
|
|
|
fileprivate func _sendInsertion(_ instance: T) async { |
|
|
|
|
await self._sendOperationBatch(OperationBatch(insert: instance)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _sendUpdate(_ instance: T) { |
|
|
|
|
self._sendOperationBatch(OperationBatch(update: instance)) |
|
|
|
|
fileprivate func _sendUpdate(_ instance: T) async { |
|
|
|
|
await self._sendOperationBatch(OperationBatch(update: instance)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _sendDeletion(_ instance: T) { |
|
|
|
|
self._sendOperationBatch(OperationBatch(delete: instance)) |
|
|
|
|
fileprivate func _sendDeletion(_ instance: T) async { |
|
|
|
|
await self._sendOperationBatch(OperationBatch(delete: instance)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _sendOperationBatch(_ batch: OperationBatch<T>) { |
|
|
|
|
Task { |
|
|
|
|
do { |
|
|
|
|
try await StoreCenter.main.sendOperationBatch(batch) |
|
|
|
|
} catch { |
|
|
|
|
Logger.error(error) |
|
|
|
|
} |
|
|
|
|
fileprivate func _sendOperationBatch(_ batch: OperationBatch<T>) async { |
|
|
|
|
do { |
|
|
|
|
try await StoreCenter.main.sendOperationBatch(batch) |
|
|
|
|
} catch { |
|
|
|
|
Logger.error(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _executeBatchOnce(_ batch: OperationBatch<T>) async { |
|
|
|
|
do { |
|
|
|
|
try await StoreCenter.main.singleBatchExecution(batch) |
|
|
|
|
} catch { |
|
|
|
|
Logger.error(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -333,11 +422,11 @@ public class SyncedCollection<T : SyncedStorable>: BaseCollection<T>, SomeSynced |
|
|
|
|
// MARK: - Others |
|
|
|
|
|
|
|
|
|
/// Sends a POST request for the instance, and changes the collection to perform a write |
|
|
|
|
public func writeChangeAndInsertOnServer(instance: T) { |
|
|
|
|
public func writeChangeAndInsertOnServer(instance: T) async { |
|
|
|
|
defer { |
|
|
|
|
self.setChanged() |
|
|
|
|
} |
|
|
|
|
self._sendInsertion(instance) |
|
|
|
|
await self._sendInsertion(instance) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|