|
|
|
@ -9,6 +9,7 @@ import Foundation |
|
|
|
|
|
|
|
|
|
|
|
enum StoredCollectionError : Error { |
|
|
|
enum StoredCollectionError : Error { |
|
|
|
case unmanagedHTTPMethod(method: String) |
|
|
|
case unmanagedHTTPMethod(method: String) |
|
|
|
|
|
|
|
case missingApiCallCollection |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protocol SomeCollection : Identifiable { |
|
|
|
protocol SomeCollection : Identifiable { |
|
|
|
@ -143,7 +144,7 @@ public class StoredCollection<T : Storable> : RandomAccessCollection, SomeCollec |
|
|
|
|
|
|
|
|
|
|
|
/// Adds or updates the provided instance inside the collection |
|
|
|
/// Adds or updates the provided instance inside the collection |
|
|
|
/// Adds it if its id is not found, and otherwise updates it |
|
|
|
/// Adds it if its id is not found, and otherwise updates it |
|
|
|
public func addOrUpdate(instance: T) { |
|
|
|
public func addOrUpdate(instance: T) throws { |
|
|
|
|
|
|
|
|
|
|
|
defer { |
|
|
|
defer { |
|
|
|
self._hasChanged = true |
|
|
|
self._hasChanged = true |
|
|
|
@ -152,11 +153,11 @@ public class StoredCollection<T : Storable> : RandomAccessCollection, SomeCollec |
|
|
|
// update |
|
|
|
// update |
|
|
|
if let index = self.items.firstIndex(where: { $0.id == instance.id }) { |
|
|
|
if let index = self.items.firstIndex(where: { $0.id == instance.id }) { |
|
|
|
self.items[index] = instance |
|
|
|
self.items[index] = instance |
|
|
|
self._sendUpdateIfNecessary(instance) |
|
|
|
try self._sendUpdateIfNecessary(instance) |
|
|
|
} else { // insert |
|
|
|
} else { // insert |
|
|
|
self.items.append(instance) |
|
|
|
self.items.append(instance) |
|
|
|
self._index?[instance.stringId] = instance |
|
|
|
self._index?[instance.stringId] = instance |
|
|
|
self._sendInsertionIfNecessary(instance) |
|
|
|
try self._sendInsertionIfNecessary(instance) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
@ -171,18 +172,18 @@ public class StoredCollection<T : Storable> : RandomAccessCollection, SomeCollec |
|
|
|
try instance.deleteDependencies() |
|
|
|
try instance.deleteDependencies() |
|
|
|
self.items.removeAll { $0.id == instance.id } |
|
|
|
self.items.removeAll { $0.id == instance.id } |
|
|
|
self._index?.removeValue(forKey: instance.stringId) |
|
|
|
self._index?.removeValue(forKey: instance.stringId) |
|
|
|
self._sendDeletionIfNecessary(instance) |
|
|
|
try self._sendDeletionIfNecessary(instance) |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Inserts the whole sequence into the items array, no updates |
|
|
|
/// Inserts the whole sequence into the items array, no updates |
|
|
|
public func append(contentOfs sequence: any Sequence<T>) { |
|
|
|
public func append(contentOfs sequence: any Sequence<T>) throws { |
|
|
|
defer { |
|
|
|
defer { |
|
|
|
self._hasChanged = true |
|
|
|
self._hasChanged = true |
|
|
|
} |
|
|
|
} |
|
|
|
self.items.append(contentsOf: sequence) |
|
|
|
self.items.append(contentsOf: sequence) |
|
|
|
for instance in sequence { |
|
|
|
for instance in sequence { |
|
|
|
self._sendUpdateIfNecessary(instance) |
|
|
|
try self._sendInsertionIfNecessary(instance) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -233,19 +234,21 @@ public class StoredCollection<T : Storable> : RandomAccessCollection, SomeCollec |
|
|
|
|
|
|
|
|
|
|
|
/// Writes all the items as a json array inside a file |
|
|
|
/// Writes all the items as a json array inside a file |
|
|
|
fileprivate func _write() { |
|
|
|
fileprivate func _write() { |
|
|
|
|
|
|
|
Logger.log("Start write...") |
|
|
|
do { |
|
|
|
do { |
|
|
|
let jsonString: String = try self.items.jsonString() |
|
|
|
let jsonString: String = try self.items.jsonString() |
|
|
|
let _ = try FileUtils.writeToDocumentDirectory(content: jsonString, fileName: T.fileName()) |
|
|
|
let _ = try FileUtils.writeToDocumentDirectory(content: jsonString, fileName: T.fileName()) |
|
|
|
} catch { |
|
|
|
} catch { |
|
|
|
Logger.error(error) // TODO how to notify the main project |
|
|
|
Logger.error(error) // TODO how to notify the main project |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Logger.log("End write") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Synchronization |
|
|
|
// MARK: - Synchronization |
|
|
|
|
|
|
|
|
|
|
|
fileprivate func _callForInstance(_ instance: T, method: Method) throws -> ApiCall<T>? { |
|
|
|
fileprivate func _callForInstance(_ instance: T, method: Method) throws -> ApiCall<T>? { |
|
|
|
guard let apiCallCollection = self.apiCallsCollection else { |
|
|
|
guard let apiCallCollection = self.apiCallsCollection else { |
|
|
|
throw StoreError.apiCallCollectionNotRegistered(type: T.resourceName()) |
|
|
|
throw StoredCollectionError.missingApiCallCollection |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if let existingCall = apiCallCollection.first(where: { $0.dataId == instance.id }) { |
|
|
|
if let existingCall = apiCallCollection.first(where: { $0.dataId == instance.id }) { |
|
|
|
@ -274,23 +277,24 @@ public class StoredCollection<T : Storable> : RandomAccessCollection, SomeCollec |
|
|
|
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) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fileprivate func _prepareCall(apiCall: ApiCall<T>) { |
|
|
|
fileprivate func _prepareCall(apiCall: ApiCall<T>) throws { |
|
|
|
apiCall.lastAttemptDate = Date() |
|
|
|
apiCall.lastAttemptDate = Date() |
|
|
|
apiCall.attemptsCount += 1 |
|
|
|
apiCall.attemptsCount += 1 |
|
|
|
self.apiCallsCollection?.addOrUpdate(instance: apiCall) |
|
|
|
try self.apiCallsCollection?.addOrUpdate(instance: apiCall) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Sends an insert api call for the provided [instance] |
|
|
|
/// Sends an insert api call for the provided [instance] |
|
|
|
fileprivate func _sendInsertionIfNecessary(_ instance: T) { |
|
|
|
fileprivate func _sendInsertionIfNecessary(_ instance: T) throws { |
|
|
|
guard self.synchronized else { |
|
|
|
guard self.synchronized else { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let apiCall = try self._callForInstance(instance, method: Method.post) { |
|
|
|
|
|
|
|
try self._prepareCall(apiCall: apiCall) |
|
|
|
|
|
|
|
|
|
|
|
Task { |
|
|
|
Task { |
|
|
|
do { |
|
|
|
do { |
|
|
|
if let apiCall = try self._callForInstance(instance, method: Method.post) { |
|
|
|
|
|
|
|
self._prepareCall(apiCall: apiCall) |
|
|
|
|
|
|
|
_ = try await self._store.execute(apiCall: apiCall) |
|
|
|
_ = try await self._store.execute(apiCall: apiCall) |
|
|
|
} |
|
|
|
|
|
|
|
} catch { |
|
|
|
} catch { |
|
|
|
self.rescheduleApiCallsIfNecessary() |
|
|
|
self.rescheduleApiCallsIfNecessary() |
|
|
|
Logger.error(error) |
|
|
|
Logger.error(error) |
|
|
|
@ -299,43 +303,55 @@ public class StoredCollection<T : Storable> : RandomAccessCollection, SomeCollec |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Sends an update api call for the provided [instance] |
|
|
|
/// Sends an update api call for the provided [instance] |
|
|
|
fileprivate func _sendUpdateIfNecessary(_ instance: T) { |
|
|
|
fileprivate func _sendUpdateIfNecessary(_ instance: T) throws { |
|
|
|
guard self.synchronized else { |
|
|
|
guard self.synchronized else { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let apiCall = try self._callForInstance(instance, method: Method.put) { |
|
|
|
|
|
|
|
try self._prepareCall(apiCall: apiCall) |
|
|
|
Task { |
|
|
|
Task { |
|
|
|
do { |
|
|
|
do { |
|
|
|
if let apiCall = try self._callForInstance(instance, method: Method.put) { |
|
|
|
|
|
|
|
self._prepareCall(apiCall: apiCall) |
|
|
|
|
|
|
|
_ = try await self._store.execute(apiCall: apiCall) |
|
|
|
_ = try await self._store.execute(apiCall: apiCall) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// let _ = try await self._store.service?.update(instance) |
|
|
|
|
|
|
|
} catch { |
|
|
|
} catch { |
|
|
|
Logger.error(error) |
|
|
|
Logger.error(error) |
|
|
|
self.rescheduleApiCallsIfNecessary() |
|
|
|
self.rescheduleApiCallsIfNecessary() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Task { |
|
|
|
|
|
|
|
// do { |
|
|
|
|
|
|
|
// if let apiCall = try self._callForInstance(instance, method: Method.put) { |
|
|
|
|
|
|
|
// try self._prepareCall(apiCall: apiCall) |
|
|
|
|
|
|
|
// _ = try await self._store.execute(apiCall: apiCall) |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
// |
|
|
|
|
|
|
|
//// let _ = try await self._store.service?.update(instance) |
|
|
|
|
|
|
|
// } catch { |
|
|
|
|
|
|
|
// Logger.error(error) |
|
|
|
|
|
|
|
// self.rescheduleApiCallsIfNecessary() |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Sends an delete api call for the provided [instance] |
|
|
|
/// Sends an delete api call for the provided [instance] |
|
|
|
fileprivate func _sendDeletionIfNecessary(_ instance: T) { |
|
|
|
fileprivate func _sendDeletionIfNecessary(_ instance: T) throws { |
|
|
|
guard self.synchronized else { |
|
|
|
guard self.synchronized else { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let apiCall = try self._callForInstance(instance, method: Method.delete) { |
|
|
|
|
|
|
|
try self._prepareCall(apiCall: apiCall) |
|
|
|
Task { |
|
|
|
Task { |
|
|
|
do { |
|
|
|
do { |
|
|
|
|
|
|
|
|
|
|
|
if let apiCall = try self._callForInstance(instance, method: Method.delete) { |
|
|
|
|
|
|
|
self._prepareCall(apiCall: apiCall) |
|
|
|
|
|
|
|
_ = try await self._store.execute(apiCall: apiCall) |
|
|
|
_ = try await self._store.execute(apiCall: apiCall) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// let _ = try await self._store.service?.delete(instance) |
|
|
|
|
|
|
|
} catch { |
|
|
|
} catch { |
|
|
|
Logger.error(error) |
|
|
|
Logger.error(error) |
|
|
|
self.rescheduleApiCallsIfNecessary() |
|
|
|
self.rescheduleApiCallsIfNecessary() |
|
|
|
@ -344,6 +360,9 @@ public class StoredCollection<T : Storable> : RandomAccessCollection, SomeCollec |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// MARK: - Reschedule calls |
|
|
|
// MARK: - Reschedule calls |
|
|
|
|
|
|
|
|
|
|
|
/// number of time an execution loop has been called |
|
|
|
/// number of time an execution loop has been called |
|
|
|
@ -422,8 +441,8 @@ public class StoredCollection<T : Storable> : RandomAccessCollection, SomeCollec |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public func append(_ newElement: T) { |
|
|
|
// public func append(_ newElement: T) { |
|
|
|
self.addOrUpdate(instance: newElement) |
|
|
|
// self.addOrUpdate(instance: newElement) |
|
|
|
} |
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|