Improve loadDataFromServer to provision the GET request

sync2
Laurent 8 months ago
parent 4c4cc246b9
commit cca9812b86
  1. 47
      LeStorage/ApiCallCollection.swift
  2. 2
      LeStorage/Codables/ApiCall.swift
  3. 2
      LeStorage/Codables/GetSyncData.swift
  4. 18
      LeStorage/Services.swift
  5. 28
      LeStorage/Store.swift
  6. 9
      LeStorage/StoreCenter.swift
  7. 24
      LeStorage/StoredCollection+Sync.swift
  8. 15
      LeStorage/StoredCollection.swift

@ -190,7 +190,7 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection {
for batch in batches.values {
do {
if batch.count == 1, let apiCall = batch.first, apiCall.method == .get {
let _: Empty = try await self._executeGetCall(apiCall)
try await self._executeGetCall(apiCall: apiCall)
} else {
let results = try await self._executeApiCalls(batch)
if T.copyServerResponse {
@ -211,6 +211,15 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection {
// Logger.log("\(T.resourceName()) > isRescheduling = \(self._isRescheduling)")
}
fileprivate func _executeGetCall(apiCall: ApiCall<T>) async throws {
if T.self == GetSyncData.self {
let _: Empty = try await StoreCenter.main.executeGet(apiCall: apiCall)
} else {
let results: [T] = try await StoreCenter.main.executeGet(apiCall: apiCall)
await StoreCenter.main.itemsRetrieved(results, storeId: apiCall.storeId)
}
}
/// Wait for an exponentionnaly long time depending on the number of attemps
fileprivate func _wait() async {
@ -282,11 +291,16 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection {
}
/// Sends an insert api call for the provided [instance]
func sendGetRequest(instance: T) async throws where T : URLParameterConvertible {
func sendGetRequest(instance: T? = nil, storeId: String? = nil) async throws {
do {
let apiCall = ApiCall<T>(method: .get, data: nil)
apiCall.urlParameters = instance.queryParameters()
let _: Empty? = try await self._prepareAndSendCall(apiCall)
if let parameteredInstance = instance as? URLParameterConvertible {
apiCall.urlParameters = parameteredInstance.queryParameters()
}
if let storeId {
apiCall.urlParameters = [Services.storeIdURLParameter : storeId]
}
try await self._prepareAndSendGetCall(apiCall)
} catch {
self.rescheduleApiCallsIfNecessary()
Logger.error(error)
@ -312,31 +326,16 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection {
return try await self._executeApiCalls(apiCalls)
}
// /// Initiates the process of sending the data with the server
//<<<<<<< HEAD
// fileprivate func _sendServerRequest<V: Decodable>(_ method: HTTPMethod, instance: T? = nil) async throws -> V? {
// if let apiCall = try await self._call(method: method, instance: instance) {
// return try await self._prepareAndSendCall(apiCall)
//=======
// fileprivate func _synchronize<V: Decodable>(_ instance: T, method: HTTPMethod) async throws -> V? {
// if let apiCall = try await self._callForInstance(instance, method: method) {
// return try await self._executeApiCall(apiCall)
//>>>>>>> main
// } else {
// return nil
// }
// }
fileprivate func _prepareAndSendCall<V: Decodable>(_ apiCall: ApiCall<T>) async throws -> V? {
fileprivate func _prepareAndSendGetCall(_ apiCall: ApiCall<T>) async throws {
self._prepareCall(apiCall: apiCall)
return try await self._executeGetCall(apiCall)
try await self._executeGetCall(apiCall: apiCall)
}
/// Executes an API call
/// For POST requests, potentially copies additional data coming from the server during the insert
fileprivate func _executeGetCall<V: Decodable>(_ apiCall: ApiCall<T>) async throws -> V {
return try await StoreCenter.main.executeGet(apiCall: apiCall)
}
// fileprivate func _executeGetCall<V: Decodable>(_ apiCall: ApiCall<T>) async throws -> V {
// return try await StoreCenter.main.executeGet(apiCall: apiCall)
// }
/// Executes an API call
/// For POST requests, potentially copies additional data coming from the server during the insert

@ -87,6 +87,8 @@ public class ApiCall<T: Storable>: ModelObject, Storable, SomeCall {
return nil
}
var storeId: String? { return self.urlParameters?[Services.storeIdURLParameter] }
public static func relationships() -> [Relationship] { return [] }
}

@ -14,7 +14,7 @@ class GetSyncData: SyncedModelObject, SyncedStorable, URLParameterConvertible {
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func resourceName() -> String {
return "sync-requests"
return "sync-data"
}
func copy(from other: any Storable) {

@ -33,7 +33,7 @@ let changePasswordCall: ServiceCall = ServiceCall(
let postDeviceTokenCall: ServiceCall = ServiceCall(
path: "device-token/", method: .post, requiresToken: true)
let getUserDataAccessCall: ServiceCall = ServiceCall(
path: "user-data-access/", method: .get, requiresToken: true)
path: "data-access/", method: .get, requiresToken: true)
let userNamesCall: ServiceCall = ServiceCall(
path: "user-names/", method: .get, requiresToken: true)
@ -52,6 +52,8 @@ public class Services {
Logger.log("create keystore with id: \(url)")
}
static let storeIdURLParameter = "store_id"
// MARK: - Base
/// Runs a request using a configuration object
@ -80,7 +82,7 @@ public class Services {
/// - Parameters:
/// - request: the URLRequest to run
/// - apiCallId: the id of the ApiCall to delete in case of success, or to schedule for a rerun in case of failure
fileprivate func _runRequest<T: SyncedStorable, V: Decodable>(
fileprivate func _runGetApiCallRequest<T: SyncedStorable, V: Decodable>(
_ request: URLRequest, apiCall: ApiCall<T>
) async throws -> V {
let debugURL = request.url?.absoluteString ?? ""
@ -242,14 +244,12 @@ public class Services {
/// - identifier: an optional StoreIdentifier that allows to filter GET requests with the StoreIdentifier values
fileprivate func _baseRequest(
servicePath: String, method: HTTPMethod, requiresToken: Bool? = nil,
identifier: String? = nil, getArguments: [String: String]? = nil
identifier: String? = nil, getArguments: [String : String]? = nil
) throws -> URLRequest {
var urlString = baseURL + servicePath
var arguments: [String:String] = getArguments ?? [:]
var arguments: [String : String] = getArguments ?? [:]
if let identifier {
arguments["store_id"] = identifier
// let component = "?store_id=\(identifier)"
// urlString.append(component)
arguments[Services.storeIdURLParameter] = identifier
}
urlString.append(arguments.toQueryString())
@ -369,7 +369,7 @@ public class Services {
/// - apiCall: An ApiCall instance to configure the returned request
fileprivate func _syncGetRequest<T: SyncedStorable>(from apiCall: ApiCall<T>) throws -> URLRequest {
var urlString = baseURL + "data/"
var urlString = "\(baseURL)\(T.resourceName())/" // baseURL + T.resourceName() // "data/"
if let urlParameters = apiCall.formattedURLParameters() {
urlString.append(urlParameters)
}
@ -547,7 +547,7 @@ public class Services {
/// Executes an ApiCall
func runGetApiCall<T: SyncedStorable, V: Decodable>(_ apiCall: ApiCall<T>) async throws -> V {
let request = try self._syncGetRequest(from: apiCall)
return try await self._runRequest(request, apiCall: apiCall)
return try await self._runGetApiCallRequest(request, apiCall: apiCall)
}
/// Executes an ApiCall

@ -281,26 +281,14 @@ final public class Store {
}
}
/// Requests an insertion to the StoreCenter
/// - Parameters:
/// - instance: an object to insert
// func sendInsertion<T: SyncedStorable>(_ instance: T) async throws -> T? {
// return try await StoreCenter.main.sendInsertion(instance)
// }
//
// /// Requests an update to the StoreCenter
// /// - Parameters:
// /// - instance: an object to update
// @discardableResult func sendUpdate<T: SyncedStorable>(_ instance: T) async throws -> T? {
// return try await StoreCenter.main.sendUpdate(instance)
// }
//
// /// Requests a deletion to the StoreCenter
// /// - Parameters:
// /// - instance: an object to delete
// func sendDeletion<T: SyncedStorable>(_ instance: T) async throws {
// return try await StoreCenter.main.sendDeletion(instance)
// }
func loadCollectionItems<T: SyncedStorable>(_ items: [T]) async {
do {
let collection: StoredCollection<T> = try self.collection()
await collection.clearAndLoadItems(items)
} catch {
Logger.error(error)
}
}
/// Returns whether all collections have loaded locally
public func fileCollectionsAllLoaded() -> Bool {

@ -442,6 +442,10 @@ public class StoreCenter {
return try await self.service().get(identifier: identifier)
}
func itemsRetrieved<T: SyncedStorable>(_ results: [T], storeId: String?) async {
await self._store(id: storeId).loadCollectionItems(results)
}
// MARK: - Synchronization
/// Creates the ApiCallCollection to manage the calls to the API
@ -485,6 +489,11 @@ public class StoreCenter {
}
func sendGetRequest<T: SyncedStorable>(_ type: T.Type, storeId: String?) async throws {
let apiCallCollection: ApiCallCollection<T> = try self.apiCallCollection()
try await apiCallCollection.sendGetRequest(storeId: storeId)
}
/// Processes Data Access data
func userDataAccessRetrieved(_ data: Data) async {
do {

@ -40,16 +40,20 @@ extension StoredCollection: SomeSyncedCollection where T : SyncedStorable {
throw StoreError.cannotSyncCollection(name: self.resourceName)
}
do {
let items: [T] = try await self.store.getItems()
if items.count > 0 {
DispatchQueue.main.async {
if clear {
self.clear()
}
self.addOrUpdateNoSync(contentOfs: items)
}
}
self.setAsLoaded()
try await StoreCenter.main.sendGetRequest(T.self, storeId: self.storeId)
// let items: [T] = try await self.store.getItems()
// if items.count > 0 {
// DispatchQueue.main.async {
// if clear {
// self.clear()
// }
// self.addOrUpdateNoSync(contentOfs: items)
// }
// }
// self.setAsLoaded()
} catch {
Logger.error(error)
}

@ -128,9 +128,6 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
if FileManager.default.fileExists(atPath: fileURL.path()) {
let jsonString: String = try FileUtils.readFile(fileURL: fileURL)
let decoded: [T] = try jsonString.decodeArray() ?? []
for item in decoded {
item.store = self.store
}
self._setItems(decoded)
}
self.setAsLoaded()
@ -148,6 +145,9 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
/// Sets a collection of items and indexes them
fileprivate func _setItems(_ items: [T]) {
for item in items {
item.store = self.store
}
self.items = items
self._updateIndexIfNecessary()
}
@ -159,6 +159,15 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
}
}
func clearAndLoadItems(_ items: [T]) async {
await MainActor.run {
self.clear()
self._setItems(items)
self.setAsLoaded()
self.setChanged()
}
}
// MARK: - Basic operations
/// Adds or updates the provided instance inside the collection

Loading…
Cancel
Save