diff --git a/Sources/FronteggSwift/FronteggAuth.swift b/Sources/FronteggSwift/FronteggAuth.swift index 841f51f..9b21868 100644 --- a/Sources/FronteggSwift/FronteggAuth.swift +++ b/Sources/FronteggSwift/FronteggAuth.swift @@ -283,7 +283,7 @@ public class FronteggAuth: ObservableObject { } guard let data = responseData else { - completion(.failure(FronteggError.authError("Failed to authenticate with frontegg"))) + completion(.failure(FronteggError.authError(.failedToAuthenticate))) setIsLoading(false) return } @@ -296,7 +296,7 @@ public class FronteggAuth: ObservableObject { completion(.success(user!)) } catch { logger.error("Failed to load user data: \(error.localizedDescription)") - completion(.failure(FronteggError.authError("Failed to load user data: \(error.localizedDescription)"))) + completion(.failure(FronteggError.authError(.failedToLoadUserData(error.localizedDescription)))) setIsLoading(false) return } @@ -315,27 +315,26 @@ public class FronteggAuth: ObservableObject { return { callbackUrl, error in - if error != nil { - completion(.failure(FronteggError.authError(error!.localizedDescription))) + if let error { + completion(.failure(FronteggError.authError(.other(error)))) return } guard let url = callbackUrl else { - let errorMessage = "Unknown error occurred" - completion(.failure(FronteggError.authError(errorMessage))) + completion(.failure(FronteggError.authError(.unknown))) return } self.logger.trace("handleHostedLoginCallback, url: \(url)") guard let queryItems = getQueryItems(url.absoluteString), let code = queryItems["code"] else { - let error = FronteggError.authError("Failed to get extract code from hostedLoginCallback url") + let error = FronteggError.authError(.failedToExtractCode) completion(.failure(error)) return } guard let codeVerifier = CredentialManager.getCodeVerifier() else { - let error = FronteggError.authError("IlligalState, codeVerifier not found") + let error = FronteggError.authError(.codeVerifierNotFound) completion(.failure(error)) return } @@ -512,7 +511,7 @@ public class FronteggAuth: ObservableObject { rootVC.present(hostingController, animated: false, completion: nil) } else { - logger.critical(FronteggError.authError("Unable to find root viewController").localizedDescription) + logger.critical(FronteggError.authError(.couldNotFindRootViewController).localizedDescription) exit(500) } } @@ -524,7 +523,7 @@ public class FronteggAuth: ObservableObject { } guard let rootVC = self.getRootVC(useAppRootVC) else { - logger.error(FronteggError.authError("Unable to find root viewController").localizedDescription) + logger.error(FronteggError.authError(.couldNotFindRootViewController).localizedDescription) return false; } @@ -574,7 +573,7 @@ public class FronteggAuth: ObservableObject { if let user = self.user, await self.refreshTokenIfNeeded() && completion != nil { completion?(.success(user)) } else { - completion?(.failure(FronteggError.authError("Failed to swift tenant"))) + completion?(.failure(FronteggError.authError(.failedToSwitchTenant))) } } diff --git a/Sources/FronteggSwift/models/errors/AuthenticationError.swift b/Sources/FronteggSwift/models/errors/AuthenticationError.swift new file mode 100644 index 0000000..8ddc4ba --- /dev/null +++ b/Sources/FronteggSwift/models/errors/AuthenticationError.swift @@ -0,0 +1,42 @@ +// +// AuthenticationError.swift +// +// +// Created by Nick Hagi on 17/07/2024. +// + +import Foundation + +// MARK: - AuthenticationError +extension FronteggError { + + public enum Authentication: LocalizedError { + case couldNotExchangeToken(_ message: String) + case failedToAuthenticate + case failedToLoadUserData(_ message: String) + case failedToExtractCode + case failedToSwitchTenant + case codeVerifierNotFound + case couldNotFindRootViewController + case unknown + case other(Error) + } +} + +// MARK: - LocalizedError +extension FronteggError.Authentication { + + public var errorDescription: String? { + switch self { + case let .couldNotExchangeToken(message): message + case .failedToAuthenticate: "Failed to authenticate with frontegg" + case let .failedToLoadUserData(message): "Failed to load user data: \(message)" + case .failedToExtractCode: "Failed to get extract code from hostedLoginCallback url" + case .failedToSwitchTenant: "Failed to switch tenant" + case .codeVerifierNotFound: "Code verifier not found" + case .couldNotFindRootViewController: "Unable to find root viewController" + case .unknown: "Unknown error occurred" + case let .other(error): error.localizedDescription + } + } +} diff --git a/Sources/FronteggSwift/models/errors/ConfigurationError.swift b/Sources/FronteggSwift/models/errors/ConfigurationError.swift new file mode 100644 index 0000000..6586ab4 --- /dev/null +++ b/Sources/FronteggSwift/models/errors/ConfigurationError.swift @@ -0,0 +1,32 @@ +// +// ConfigurationError.swift +// +// +// Created by Nick Hagi on 17/07/2024. +// + +import Foundation + +// MARK: - ConfigurationError +extension FronteggError { + + public enum Configuration: LocalizedError { + case missingPlist + case missingClientIdOrBaseURL(_ atPath: String) + case missingRegions + case invalidRegions(_ atPath: String) + } +} + +// MARK: - LocalizedError +extension FronteggError.Configuration { + + public var errorDescription: String? { + switch self { + case .missingPlist: "Missing Frontegg.plist file with 'clientId' and 'baseUrl' entries in main bundle!" + case let .missingClientIdOrBaseURL(path): "Frontegg.plist file at \(path) is missing 'clientId' and/or 'baseUrl' entries!" + case .missingRegions: "no regions in Frontegg.plist" + case let .invalidRegions(path): "Frontegg.plist file at \(path) has invalid regions data, regions must be array of (key, baseUrl, clientId)" + } + } +} diff --git a/Sources/FronteggSwift/models/errors/FronteggError.swift b/Sources/FronteggSwift/models/errors/FronteggError.swift new file mode 100644 index 0000000..2213016 --- /dev/null +++ b/Sources/FronteggSwift/models/errors/FronteggError.swift @@ -0,0 +1,13 @@ +// +// FronteggError.swift +// +// +// Created by Nick Hagi on 17/07/2024. +// + +import Foundation + +public enum FronteggError: Error { + case configError(Configuration) + case authError(Authentication) +} diff --git a/Sources/FronteggSwift/services/Api.swift b/Sources/FronteggSwift/services/Api.swift index 38ce797..0e15d56 100644 --- a/Sources/FronteggSwift/services/Api.swift +++ b/Sources/FronteggSwift/services/Api.swift @@ -138,7 +138,7 @@ public class Api { return (try JSONDecoder().decode(AuthResponse.self, from: data), nil) }catch { - return (nil, FronteggError.authError(error.localizedDescription)) + return (nil, FronteggError.authError(.couldNotExchangeToken(error.localizedDescription))) } } diff --git a/Sources/FronteggSwift/utils/PlistHelper.swift b/Sources/FronteggSwift/utils/PlistHelper.swift index ea676b4..aeb037a 100644 --- a/Sources/FronteggSwift/utils/PlistHelper.swift +++ b/Sources/FronteggSwift/utils/PlistHelper.swift @@ -7,13 +7,6 @@ import Foundation - -public enum FronteggError: Error { - case configError(String) - case authError(String) -} - - public struct RegionConfig { public var key: String public var baseUrl: String @@ -31,7 +24,8 @@ public struct RegionConfig { struct PlistHelper { private static var logLevelCache: Logger.Level? = nil - + private static var logger = getLogger("PlistHelper") + public static func fronteggConfig() throws -> (clientId: String, baseUrl: String, applicationId: String?, keychainService: String, bundleIdentifier: String) { let bundle = Bundle.main; @@ -39,15 +33,15 @@ struct PlistHelper { guard let path = bundle.path(forResource: resourceName, ofType: "plist"), let values = NSDictionary(contentsOfFile: path) as? [String: Any] else { - let errorMessage = "Missing Frontegg.plist file with 'clientId' and 'baseUrl' entries in main bundle!" - print(errorMessage) - throw FronteggError.configError(errorMessage) + let error = FronteggError.configError(.missingPlist) + logger.debug(error.localizedDescription) + throw error } guard let clientId = values["clientId"] as? String, let baseUrl = values["baseUrl"] as? String else { - let errorMessage = "Frontegg.plist file at \(path) is missing 'clientId' and/or 'baseUrl' entries!" - print(errorMessage) - throw FronteggError.configError(errorMessage) + let error = FronteggError.configError(.missingClientIdOrBaseURL(path)) + logger.debug(error.localizedDescription) + throw error } let applicationId = values["applicationId"] as? String @@ -64,28 +58,34 @@ struct PlistHelper { guard let path = bundle.path(forResource: resourceName, ofType: "plist"), let values = NSDictionary(contentsOfFile: path) as? [String: Any] else { - let errorMessage = "Missing Frontegg.plist file with 'clientId' and 'baseUrl' entries in main bundle!" - print(errorMessage) - throw FronteggError.configError(errorMessage) - } - - guard let regions = values["regions"] as? [[String: String]] else { - throw FronteggError.configError("no regions in Frontegg.plist") + let error = FronteggError.configError(.missingPlist) + logger.debug(error.localizedDescription) + throw error } - if ( regions.count == 0 ) { - throw FronteggError.configError("no regions in Frontegg.plist") + guard + let regions = values["regions"] as? [[String: String]], + !regions.isEmpty + else { + let error = FronteggError.configError(.missingRegions) + logger.debug(error.localizedDescription) + throw error } let keychainService = values["keychainService"] as? String ?? "frontegg" let regionConfigs = try regions.map { dict in - guard let key = dict["key"], - let baseUrl = dict["baseUrl"], - let clientId = dict["clientId"] else { - throw FronteggError.configError("Frontegg.plist file at \(path) has invalid regions data, regions must be array of (key, baseUrl, clientId)") + guard + let key = dict["key"], + let baseUrl = dict["baseUrl"], + let clientId = dict["clientId"] + else { + let error = FronteggError.configError(.invalidRegions(path)) + logger.debug(error.localizedDescription) + throw error } - let applicationId = dict["applicationId"] + + let applicationId = dict["applicationId"] return RegionConfig(key: key, baseUrl: baseUrl, clientId: clientId, applicationId: applicationId) } return (regions: regionConfigs, keychainService: keychainService, bundleIdentifier: bundle.bundleIdentifier!)