sync2
Laurent 12 months ago
commit 23a34838e5
  1. 24
      LeStorage/ApiCallCollection.swift
  2. 2
      LeStorage/Services.swift
  3. 21
      LeStorage/Store.swift
  4. 11
      LeStorage/StoredCollection.swift
  5. 28
      LeStorage/Utils/Errors.swift
  6. 9
      LeStorage/Utils/FileUtils.swift
  7. 11
      LeStorage/Utils/KeychainStore.swift

@ -20,6 +20,17 @@ protocol SomeCallCollection {
}
enum ApiCallError: Error, LocalizedError {
case encodingError(id: String, type: String)
var errorDescription: String? {
switch self {
case .encodingError(let id, let type):
return "Can't encode instance \(type) with id: \(id)"
}
}
}
/// ApiCallCollection is an object communicating with a server to synchronize data managed locally
/// The Api calls are serialized and stored in a JSON file
/// Failing Api calls are stored forever and will be executed again later
@ -267,7 +278,7 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection {
}
/// 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>) {
apiCall.lastAttemptDate = Date()
apiCall.attemptsCount += 1
self.addOrUpdate(apiCall)
@ -291,6 +302,7 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection {
return try await self._sendServerRequest(HTTPMethod.post, instance: instance)
} catch {
self.rescheduleApiCallsIfNecessary()
StoreCenter.main.log(message: "POST failed for \(instance): \(error.localizedDescription)")
Logger.error(error)
}
return nil
@ -303,6 +315,7 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection {
return try await self._sendServerRequest(HTTPMethod.put, instance: instance)
} catch {
self.rescheduleApiCallsIfNecessary()
StoreCenter.main.log(message: "PUT failed for \(instance): \(error.localizedDescription)")
Logger.error(error)
}
return nil
@ -314,6 +327,7 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection {
let _: Empty? = try await self._sendServerRequest(HTTPMethod.delete, instance: instance)
} catch {
self.rescheduleApiCallsIfNecessary()
StoreCenter.main.log(message: "DELETE failed for \(instance): \(error.localizedDescription)")
Logger.error(error)
}
return
@ -331,16 +345,10 @@ actor ApiCallCollection<T: SyncedStorable>: SomeCallCollection {
}
fileprivate func _prepareAndSendCall<V: Decodable>(_ apiCall: ApiCall<T>) async throws -> V? {
try self._prepareCall(apiCall: apiCall)
self._prepareCall(apiCall: apiCall)
return try await self._executeApiCall(apiCall)
}
/// Executes an API call
/// For POST requests, potentially copies additional data coming from the server during the insert
// fileprivate func _executeApiCall(_ apiCall: ApiCall<T>) async throws -> T {
// return try await StoreCenter.main.execute(apiCall: apiCall)
// }
/// Executes an API call
/// For POST requests, potentially copies additional data coming from the server during the insert
fileprivate func _executeApiCall<V: Decodable>(_ apiCall: ApiCall<T>) async throws -> V {

@ -599,7 +599,7 @@ public class Services {
/// - password: the account's password
public func deleteAccount() async throws {
guard let userId = StoreCenter.main.userId else {
throw ServiceError.missingUserId
throw StoreError.missingUserId
}
let path = "users/\(userId)/"
let deleteAccount = ServiceCall(path: path, method: .delete, requiresToken: true)

@ -8,13 +8,28 @@
import Foundation
import UIKit
public enum StoreError: Error {
public enum StoreError: Error, LocalizedError {
case missingService
case missingUserId
case unexpectedCollectionType(name: String)
case apiCallCollectionNotRegistered(type: String)
case collectionNotRegistered(type: String)
case cannotSyncCollection(name: String)
case apiCallCollectionNotRegistered(type: String)
public var errorDescription: String? {
switch self {
case .missingService:
return "Services instance is nil"
case .missingUserId:
return "The user id is missing"
case .collectionNotRegistered(let type):
return "The collection \(type) is not registered"
case .cannotSyncCollection(let name):
return "Tries to load the collection \(name) from the server while it's not authorized"
case .apiCallCollectionNotRegistered(let type):
return "The api call collection has not been registered for \(type)"
}
}
}
//public struct StoreIdentifier {

@ -7,12 +7,6 @@
import Foundation
enum StoredCollectionError: Error {
case unmanagedHTTPMethod(method: String)
case missingApiCallCollection
case missingInstance
}
protocol CollectionHolder {
associatedtype Item
@ -334,14 +328,13 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
/// Writes all the items as a json array inside a file
fileprivate func _write() {
// Logger.log("Start write to \(T.fileName())...")
do {
let jsonString: String = try self.items.jsonString()
try self.store.write(content: jsonString, fileName: T.fileName())
} catch {
Logger.error(error) // TODO how to notify the main project
Logger.error(error)
StoreCenter.main.log(message: "write failed for \(T.resourceName()): \(error.localizedDescription)")
}
// Logger.log("End write")
}
/// Simply clears the items of the collection

@ -20,17 +20,39 @@ public class ErrorUtils {
}
public enum ServiceError: Error {
public enum ServiceError: Error, LocalizedError {
case urlCreationError(url: String)
case cantConvertToUUID(id: String)
case missingUserName
case missingUserId
case responseError(response: String)
case cantDecodeData(resource: String, method: String, content: String?)
public var errorDescription: String? {
switch self {
case .urlCreationError(let url):
return "Can't create URL from \(url)"
case .cantConvertToUUID(let id):
return "Cant convert \(id) to UUID"
case .missingUserName:
return "There is no userName defined in the Settings"
case .responseError(let response):
return "The server returned an error: \(response)"
case .cantDecodeData(let resource, let method, let content):
return "cannot decode data from \(resource), method: \(method): \(content ?? "")"
}
}
}
public enum UUIDError: Error {
public enum UUIDError: Error, LocalizedError {
case cantConvertString(string: String)
public var errorDescription: String? {
switch self {
case .cantConvertString(let string):
return "cant convert string to UUID: \(string)"
}
}
}
public enum LeStorageError: Error {

@ -7,8 +7,15 @@
import Foundation
enum FileError : Error {
enum FileError: Error, LocalizedError {
case documentDirectoryNotFound
var errorDescription: String? {
switch self {
case .documentDirectoryNotFound:
return "The document directory has not been found"
}
}
}
class FileUtils {

@ -11,6 +11,17 @@ enum KeychainError: Error {
case keychainItemNotFound(serverId: String)
case unexpectedPasswordData
case unhandledError(status: OSStatus)
var errorDescription: String? {
switch self {
case .keychainItemNotFound(let serverId):
return "The keychainItem was not found: \(serverId)"
case .unexpectedPasswordData:
return "Keychain error: The data could not be converted to string"
case .unhandledError(let status):
return "Keychain error: Unmanaged status: \(status)"
}
}
}
class KeychainStore {

Loading…
Cancel
Save