diff --git a/LeStorage/ApiCallCollection.swift b/LeStorage/ApiCallCollection.swift index f61630a..ab3e6d7 100644 --- a/LeStorage/ApiCallCollection.swift +++ b/LeStorage/ApiCallCollection.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: SomeCallCollection { } /// Prepares a call for execution by updating its properties and adding it to its collection for storage - fileprivate func _prepareCall(apiCall: ApiCall) throws { + fileprivate func _prepareCall(apiCall: ApiCall) { apiCall.lastAttemptDate = Date() apiCall.attemptsCount += 1 self.addOrUpdate(apiCall) @@ -291,6 +302,7 @@ actor ApiCallCollection: 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: 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: 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: SomeCallCollection { } fileprivate func _prepareAndSendCall(_ apiCall: ApiCall) 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) 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(_ apiCall: ApiCall) async throws -> V { diff --git a/LeStorage/Services.swift b/LeStorage/Services.swift index ede9d9c..792bfa9 100644 --- a/LeStorage/Services.swift +++ b/LeStorage/Services.swift @@ -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) diff --git a/LeStorage/Store.swift b/LeStorage/Store.swift index 5577ffd..26843d4 100644 --- a/LeStorage/Store.swift +++ b/LeStorage/Store.swift @@ -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 { diff --git a/LeStorage/StoredCollection.swift b/LeStorage/StoredCollection.swift index 5d0c5b6..13a0ef1 100644 --- a/LeStorage/StoredCollection.swift +++ b/LeStorage/StoredCollection.swift @@ -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: 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 diff --git a/LeStorage/Utils/Errors.swift b/LeStorage/Utils/Errors.swift index 81eab21..01947c0 100644 --- a/LeStorage/Utils/Errors.swift +++ b/LeStorage/Utils/Errors.swift @@ -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 { diff --git a/LeStorage/Utils/FileUtils.swift b/LeStorage/Utils/FileUtils.swift index 5edf902..c906ab4 100644 --- a/LeStorage/Utils/FileUtils.swift +++ b/LeStorage/Utils/FileUtils.swift @@ -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 { diff --git a/LeStorage/Utils/KeychainStore.swift b/LeStorage/Utils/KeychainStore.swift index 0f43c57..7e650b6 100644 --- a/LeStorage/Utils/KeychainStore.swift +++ b/LeStorage/Utils/KeychainStore.swift @@ -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 {