diff --git a/KiaExtension/CarListHandler.swift b/KiaExtension/CarListHandler.swift index ed14805..2680368 100644 --- a/KiaExtension/CarListHandler.swift +++ b/KiaExtension/CarListHandler.swift @@ -9,6 +9,7 @@ import Foundation import Intents import UIKit +import os.log class CarListHandler: NSObject, INListCarsIntentHandling, Handler { private let api: Api @@ -86,7 +87,7 @@ extension Vehicle { // Get Bluetooth and iAP2 identifiers for this vehicle let headUnitIds = headUnitIdentifiers() - print("CarListHandler: Vehicle '\(nickname)' - Bluetooth: \(headUnitIds.bluetooth ?? "none"), iAP2: \(headUnitIds.iap2 ?? "none")") + os_log(.debug, log: Logger.extension, "CarListHandler: Vehicle '%{public}@' - Bluetooth: %{public}@, iAP2: %{public}@", nickname, headUnitIds.bluetooth ?? "none", headUnitIds.iap2 ?? "none") let car: INCar = .init( carIdentifier: vehicleId.uuidString, diff --git a/KiaExtension/CredentialsHandler.swift b/KiaExtension/CredentialsHandler.swift index 191f526..489d21b 100644 --- a/KiaExtension/CredentialsHandler.swift +++ b/KiaExtension/CredentialsHandler.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log class CredentialsHandler { private let api: Api @@ -37,7 +38,7 @@ class CredentialsHandler { } do { - print("CredentialsHandler: Using credentials from local server for reauthorization") + os_log(.info, log: Logger.extension, "CredentialsHandler: Using credentials from local server for reauthorization") let authorization = try await api.login( username: credentials.username, password: credentials.password @@ -55,7 +56,7 @@ class CredentialsHandler { } private func updateCredentials() async { - print("CredentialsHandler: Updating credentials") + os_log(.info, log: Logger.extension, "CredentialsHandler: Updating credentials") if let credentials = try? await credentialClient.fetchCredentials() { api.authorization = credentials.authorization @@ -67,7 +68,7 @@ class CredentialsHandler { // Store username and password if available for future use if let username = credentials.username, let password = credentials.password { - print("CredentialsHandler: Successfully received username and password from local server") + os_log(.info, log: Logger.extension, "CredentialsHandler: Successfully received username and password from local server") LoginCredentialManager.store( credentials: LoginCredentials( username: username, @@ -75,9 +76,9 @@ class CredentialsHandler { ) ) } - print("CredentialsHandler: Successfully updated authorization from local server") + os_log(.info, log: Logger.extension, "CredentialsHandler: Successfully updated authorization from local server") } else { - print("CredentialsHandler: Failed to fetch credentials from local server") + os_log(.default, log: Logger.extension, "CredentialsHandler: Failed to fetch credentials from local server") // Fallback to locally stored in keychain api.authorization = Authorization.authorization } diff --git a/KiaMaps/App/ApiExtensions.swift b/KiaMaps/App/ApiExtensions.swift index 10b8b5f..4f5d8ce 100644 --- a/KiaMaps/App/ApiExtensions.swift +++ b/KiaMaps/App/ApiExtensions.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log /// Extension to Api class that handles automatic token refresh and retry logic extension Api { @@ -22,15 +23,15 @@ extension Api { } catch { // Check if this is an unauthorized error (401) indicating expired token if let apiError = error as? ApiError, case .unauthorized = apiError { - print("ApiAutoRefresh: Detected expired token, attempting automatic login retry") + os_log(.info, log: Logger.auth, "Detected expired token, attempting automatic login retry") // Try to refresh the token using stored credentials if await refreshTokenIfPossible() { - print("ApiAutoRefresh: Token refreshed successfully, retrying operation") + os_log(.info, log: Logger.auth, "Token refreshed successfully, retrying operation") // Retry the operation with fresh token return try await operation() } else { - print("ApiAutoRefresh: Token refresh failed, throwing original error") + os_log(.error, log: Logger.auth, "Token refresh failed, throwing original error") // If refresh failed, throw the original unauthorized error throw error } @@ -46,18 +47,18 @@ extension Api { private func refreshTokenIfPossible() async -> Bool { // Check if we have stored login credentials guard let storedCredentials = LoginCredentialManager.retrieveCredentials() else { - print("ApiAutoRefresh: No stored credentials available for token refresh") + os_log(.default, log: Logger.auth, "No stored credentials available for token refresh") return false } // Check if current token is actually expired before attempting refresh if let currentAuth = authorization, !isTokenExpired(currentAuth) { - print("ApiAutoRefresh: Current token is not expired, no refresh needed") + os_log(.debug, log: Logger.auth, "Current token is not expired, no refresh needed") return true } do { - print("ApiAutoRefresh: Attempting to login with stored credentials") + os_log(.info, log: Logger.auth, "Attempting to login with stored credentials") let newAuthData = try await login( username: storedCredentials.username, password: storedCredentials.password @@ -67,15 +68,15 @@ extension Api { Authorization.store(data: newAuthData) self.authorization = newAuthData - print("ApiAutoRefresh: Successfully refreshed token") + os_log(.info, log: Logger.auth, "Successfully refreshed token") return true } catch { - print("ApiAutoRefresh: Failed to refresh token with error: \(error)") + os_log(.error, log: Logger.auth, "Failed to refresh token with error: %{public}@", error.localizedDescription) // If login fails, clear the stored credentials as they might be invalid if error is ApiError { - print("ApiAutoRefresh: Clearing invalid stored credentials") + os_log(.default, log: Logger.auth, "Clearing invalid stored credentials") LoginCredentialManager.clearCredentials() } diff --git a/KiaMaps/App/AppDelegate.swift b/KiaMaps/App/AppDelegate.swift index a74bf3e..bb4399b 100644 --- a/KiaMaps/App/AppDelegate.swift +++ b/KiaMaps/App/AppDelegate.swift @@ -1,6 +1,7 @@ import SwiftUI import UIKit import BackgroundTasks +import os.log class AppDelegate: NSObject, UIApplicationDelegate { var localClient: LocalCredentialClient! = nil @@ -14,7 +15,7 @@ class AppDelegate: NSObject, UIApplicationDelegate { // Start the local credential server LocalCredentialServer.shared.start { success in - print("AppDelegate: Server start success: \(success)") + os_log(.info, log: Logger.server, "Server start success: %{public}@", success ? "true" : "false") } return true } @@ -23,7 +24,7 @@ class AppDelegate: NSObject, UIApplicationDelegate { // Stop the local credential server LocalCredentialServer.shared.stop() BackgroundTaskManager.shared.cleanup() - print("AppDelegate: Stopped local credential server") + os_log(.info, log: Logger.server, "Stopped local credential server") } } @@ -42,11 +43,11 @@ class BackgroundTaskManager: ObservableObject { BGTaskScheduler.shared.register(forTaskWithIdentifier: Self.backgroundTaskIdentifier, using: nil) { task in self.handleBackgroundServerMaintenance(task: task as! BGAppRefreshTask) } - print("BackgroundTaskManager: Registered background task: \(Self.backgroundTaskIdentifier)") + os_log(.info, log: Logger.app, "Registered background task: %{public}@", Self.backgroundTaskIdentifier) } func handleAppDidEnterBackground() { - print("BackgroundTaskManager: App entered background, maintaining server") + os_log(.info, log: Logger.app, "App entered background, maintaining server") // Start background task to keep server running startBackgroundTask() @@ -56,7 +57,7 @@ class BackgroundTaskManager: ObservableObject { } func handleAppWillEnterForeground() { - print("BackgroundTaskManager: App entering foreground") + os_log(.info, log: Logger.app, "App entering foreground") // End background task if running endBackgroundTask() @@ -77,27 +78,27 @@ class BackgroundTaskManager: ObservableObject { do { try BGTaskScheduler.shared.submit(request) - print("BackgroundTaskManager: Scheduled background refresh task") + os_log(.info, log: Logger.app, "Scheduled background refresh task") } catch { - print("BackgroundTaskManager: Failed to schedule background refresh: \(error)") + os_log(.error, log: Logger.app, "Failed to schedule background refresh: %{public}@", error.localizedDescription) } } private func handleBackgroundServerMaintenance(task: BGAppRefreshTask) { - print("BackgroundTaskManager: Handling background server maintenance") + os_log(.info, log: Logger.app, "Handling background server maintenance") // Schedule next refresh scheduleBackgroundRefresh() task.expirationHandler = { - print("BackgroundTaskManager: Background task expired") + os_log(.info, log: Logger.app, "Background task expired") task.setTaskCompleted(success: false) } // Check and restart server if needed DispatchQueue.global(qos: .default).async { if !LocalCredentialServer.shared.isRunning { - print("BackgroundTaskManager: Restarting server during background refresh") + os_log(.info, log: Logger.app, "Restarting server during background refresh") LocalCredentialServer.shared.start() // Wait a moment for server to start @@ -113,7 +114,7 @@ class BackgroundTaskManager: ObservableObject { private func startBackgroundTask() { backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "LocalServerMaintenance") { [weak self] in - print("BackgroundTaskManager: Background task expired, ending task") + os_log(.info, log: Logger.app, "Background task expired, ending task") self?.endBackgroundTask() } @@ -132,13 +133,13 @@ class BackgroundTaskManager: ObservableObject { } private func maintainServerInBackground() { - print("BackgroundTaskManager: Maintaining server in background") + os_log(.info, log: Logger.app, "Maintaining server in background") // Keep the server alive for as long as possible in background while backgroundTask != .invalid && UIApplication.shared.backgroundTimeRemaining > 5.0 { // Check server status periodically if !LocalCredentialServer.shared.isRunning { - print("BackgroundTaskManager: Restarting server in background") + os_log(.info, log: Logger.app, "Restarting server in background") LocalCredentialServer.shared.start() } @@ -146,7 +147,7 @@ class BackgroundTaskManager: ObservableObject { Thread.sleep(forTimeInterval: 1.0) } - print("BackgroundTaskManager: Background maintenance ending, time remaining: \(UIApplication.shared.backgroundTimeRemaining)") + os_log(.info, log: Logger.app, "Background maintenance ending, time remaining: %{public}f", UIApplication.shared.backgroundTimeRemaining) } } @@ -179,19 +180,19 @@ struct Application: App { private func handleScenePhaseChange(_ phase: ScenePhase) { switch phase { case .active: - print("Application: Scene became active") + os_log(.debug, log: Logger.app, "Scene became active") backgroundManager.handleAppWillEnterForeground() case .inactive: - print("Application: Scene became inactive") + os_log(.debug, log: Logger.app, "Scene became inactive") // Handle brief inactive state (e.g., incoming call, control center) case .background: - print("Application: Scene entered background") + os_log(.debug, log: Logger.app, "Scene entered background") backgroundManager.handleAppDidEnterBackground() @unknown default: - print("Application: Unknown scene phase: \(phase)") + os_log(.default, log: Logger.app, "Unknown scene phase: %{public}@", String(describing: phase)) } } } diff --git a/KiaMaps/App/LoginView.swift b/KiaMaps/App/LoginView.swift index 84a08a2..4553632 100644 --- a/KiaMaps/App/LoginView.swift +++ b/KiaMaps/App/LoginView.swift @@ -7,6 +7,7 @@ // import SwiftUI +import os.log struct LoginView: View { @State private var username: String = "" @@ -291,6 +292,6 @@ struct KiaTextFieldStyle: TextFieldStyle { #Preview { LoginView(configuration: AppConfiguration.self) { authData in - print("Login successful with user: \(authData)") + os_log(.info, log: Logger.auth, "Login successful for user") } } diff --git a/KiaMaps/App/MainView.swift b/KiaMaps/App/MainView.swift index 40643d6..74e1154 100644 --- a/KiaMaps/App/MainView.swift +++ b/KiaMaps/App/MainView.swift @@ -7,6 +7,7 @@ // import SwiftUI +import os.log struct MainView: View { let configuration: AppConfiguration.Type @@ -293,7 +294,7 @@ struct MainView: View { await loadData() if lastUpdateDate < selectedVehicleStatus.lastUpdateTime { self.lastUpdateDate = nil - print("Updated") + os_log(.debug, log: Logger.ui, "Vehicle status updated") } } else { _ = try await api.refreshVehicleWithAutoRefresh(selectedVehicle.vehicleId) diff --git a/KiaMaps/Core/Api/Api.swift b/KiaMaps/Core/Api/Api.swift index e67c7fc..ee276fd 100644 --- a/KiaMaps/Core/Api/Api.swift +++ b/KiaMaps/Core/Api/Api.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log /** * Api - Main interface for Kia/Hyundai/Genesis vehicle API communication @@ -76,15 +77,15 @@ class Api { let referer: String do { referer = try await fetchConnectorAuthorization() - print("Retrieved referer: \(referer)") + os_log(.info, log: Logger.api, "Retrieved referer: %{private}@", referer) } catch { - print("Client connector authorization failed \(error.localizedDescription)") + os_log(.error, log: Logger.api, "Client connector authorization failed: %{public}@", error.localizedDescription) throw AuthenticationError.clientConfigurationFailed } // Step 1: Get client configuration let clientConfig = try await fetchClientConfiguration(referer: referer) - print("Client configured for: \(clientConfig.clientName)") + os_log(.info, log: Logger.api, "Client configured for: %{public}@", clientConfig.clientName) // Step 2: Check if password encryption is enabled let encryptionSettings = try await fetchPasswordEncryptionSettings(referer: referer) @@ -97,7 +98,7 @@ class Api { do { rsaKey = try await fetchRSACertificate(referer: referer) } catch { - print("Fetch RSA Certificate failed \(error.localizedDescription)") + os_log(.error, log: Logger.api, "Fetch RSA Certificate failed: %{public}@", error.localizedDescription) throw AuthenticationError.certificateRetrievalFailed } // Step 4: Initialize OAuth2 flow @@ -117,7 +118,7 @@ class Api { do { tokenResponse = try await exchangeCodeForTokens(authorizationCode: authorizationCode) } catch { - print("Exchange code for token failed \(error.localizedDescription)") + os_log(.error, log: Logger.api, "Exchange code for token failed: %{public}@", error.localizedDescription) throw AuthenticationError.tokenExchangeFailed } @@ -145,9 +146,9 @@ class Api { func logout() async throws { do { try await provider.request(with: .post, endpoint: .logout).empty() - print("Successfully logout") + os_log(.info, log: Logger.auth, "Successfully logout") } catch { - print("Failed to logout: " + error.localizedDescription) + os_log(.error, log: Logger.auth, "Failed to logout: %{public}@", error.localizedDescription) } provider.authorization = nil cleanCookies() diff --git a/KiaMaps/Core/Api/ApiRequest.swift b/KiaMaps/Core/Api/ApiRequest.swift index 70f51bb..2213c7f 100644 --- a/KiaMaps/Core/Api/ApiRequest.swift +++ b/KiaMaps/Core/Api/ApiRequest.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log public enum JSONDecoders { public static let `default`: JSONDecoder = { @@ -334,13 +335,13 @@ struct ApiRequestImpl: ApiRequest { guard let string = String(data: data, encoding: .utf8) else { throw URLError(.cannotDecodeContentData) } - print("\(endpoint) - result: \(string)") + os_log(.debug, log: Logger.api, "%{public}@ - result: %{private}@", String(describing: endpoint), string) return string } func httpResponse(acceptStatusCode: Int) async throws -> HTTPURLResponse { let (_, response) = try await callRequest(acceptStatusCode: acceptStatusCode) - print("\(endpoint) - result: \(response)") + os_log(.debug, log: Logger.api, "%{public}@ - result: %{private}@", String(describing: endpoint), String(describing: response)) guard let response = response as? HTTPURLResponse else { throw URLError(.cannotDecodeContentData) } @@ -350,7 +351,7 @@ struct ApiRequestImpl: ApiRequest { func data(acceptStatusCode: Int) async throws -> Data { let (data, _) = try await callRequest(acceptStatusCode: acceptStatusCode) let result = try JSONDecoders.default.decode(Data.self, from: data) - print("\(endpoint) - result: \(result)") + os_log(.debug, log: Logger.api, "%{public}@ - result: %{private}@", String(describing: endpoint), String(describing: result)) return result } @@ -367,10 +368,10 @@ struct ApiRequestImpl: ApiRequest { @discardableResult private func callRequest(acceptStatusCode: Int) async throws -> (Data, URLResponse) { let urlRequest = try self.urlRequest - print("\(endpoint) - request: \(String(describing: urlRequest.url)) \(String(describing: urlRequest.allHTTPHeaderFields))") + os_log(.debug, log: Logger.api, "%{public}@ - request: %{private}@ %{private}@", String(describing: endpoint), String(describing: urlRequest.url), String(describing: urlRequest.allHTTPHeaderFields)) let (data, response) = try await caller.urlSession.data(for: urlRequest) - print("\(endpoint) - response: \(response)") + os_log(.debug, log: Logger.api, "%{public}@ - response: %{private}@", String(describing: endpoint), String(describing: response)) guard (200 ... 399).contains(response.status ?? 0) else { if response.status == 401 { diff --git a/KiaMaps/Core/Api/Models/SignInResponse.swift b/KiaMaps/Core/Api/Models/SignInResponse.swift index 7acd40a..a892269 100644 --- a/KiaMaps/Core/Api/Models/SignInResponse.swift +++ b/KiaMaps/Core/Api/Models/SignInResponse.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log struct SignInResponse: Decodable { let redirectUrl: URL @@ -30,7 +31,7 @@ struct SignInResponse: Decodable { return String(string[range]).replacingOccurrences(of: "&", with: "&") } } catch { - print("Invalid regex: \(error.localizedDescription)") + os_log(.error, log: Logger.api, "Invalid regex: %{public}@", error.localizedDescription) } return nil } diff --git a/KiaMaps/Core/Authorization/Authorization.swift b/KiaMaps/Core/Authorization/Authorization.swift index 5ce2656..72f5fd6 100644 --- a/KiaMaps/Core/Authorization/Authorization.swift +++ b/KiaMaps/Core/Authorization/Authorization.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log struct AuthorizationData: Codable { var stamp: String @@ -35,7 +36,7 @@ struct AuthorizationData: Codable { cfb.count == rawString.count, let rawData = rawString.data(using: .utf8) else { - print("cfb and raw length not equal") + os_log(.error, log: Logger.auth, "CFB and raw length not equal") return "" } diff --git a/KiaMaps/Core/Authorization/Keychain.swift b/KiaMaps/Core/Authorization/Keychain.swift index 65c0b25..ed3164a 100644 --- a/KiaMaps/Core/Authorization/Keychain.swift +++ b/KiaMaps/Core/Authorization/Keychain.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log private enum KeychainSecurityKeys: String { case className @@ -82,7 +83,7 @@ struct Keychain { checkForErrors("Store failed to delete value at path: \(path).", status: deleteStatus) checkForErrors("Store failed to add value at path: \(path).", status: addStatus) } catch { - print("Failed to encode: \(value)") + os_log(.error, log: Logger.keychain, "Failed to encode a value for storing into the keychain.") } } else { removeVakue(at: path) @@ -125,14 +126,14 @@ struct Keychain { } guard let data = retrievedData as? Data else { - print("Failed to cast value at path: \(path) to type Data.") + os_log(.error, log: Logger.keychain, "Failed to cast value at path: %{public}@ to type Data", String(describing: path)) return nil } do { return try JSONDecoders.default.decode(Content.self, from: data) } catch { - print("Failed to decode value at path: \(path) from type Data. \(error)") + os_log(.error, log: Logger.keychain, "Failed to decode value at path: %{public}@ from type Data: %{public}@", String(describing: path), error.localizedDescription) return nil } } @@ -147,7 +148,7 @@ struct Keychain { ].compactMap { $0 } guard !ignoredStatuses.contains(status) else { return } - print(message + ", error: \(status)") + os_log(.error, log: Logger.keychain, "%{public}@, error: %{public}d", message, status) } } diff --git a/KiaMaps/Core/Bluetooth/BluetoothManager.swift b/KiaMaps/Core/Bluetooth/BluetoothManager.swift index 7c77731..fd58336 100644 --- a/KiaMaps/Core/Bluetooth/BluetoothManager.swift +++ b/KiaMaps/Core/Bluetooth/BluetoothManager.swift @@ -9,6 +9,7 @@ import Foundation import CoreBluetooth import ExternalAccessory +import os.log /// Manages Bluetooth device discovery and identifies potential vehicle head units class BluetoothManager: NSObject { @@ -47,7 +48,7 @@ class BluetoothManager: NSObject { name: accessory.name, identifier: accessory.serialNumber )) - print("BluetoothManager: Found automotive accessory: \(accessory.name) - \(accessory.serialNumber)") + os_log(.info, log: Logger.bluetooth, "Found automotive accessory: %{public}@ - %{private}@", accessory.name, accessory.serialNumber) } } @@ -117,7 +118,7 @@ extension BluetoothManager: CBCentralManagerDelegate { func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: - print("BluetoothManager: Bluetooth is powered on") + os_log(.info, log: Logger.bluetooth, "Bluetooth is powered on") // Retrieve connected peripherals let connectedPeripherals = central.retrieveConnectedPeripherals(withServices: []) self.connectedDevices = connectedPeripherals @@ -127,17 +128,17 @@ extension BluetoothManager: CBCentralManagerDelegate { let name = peripheral.name ?? "Unknown Device" let identifier = peripheral.identifier.uuidString deviceIdentifiers[name] = identifier - print("BluetoothManager: Connected device: \(name) - \(identifier)") + os_log(.info, log: Logger.bluetooth, "Connected device: %{public}@ - %{private}@", name, identifier) } case .poweredOff: - print("BluetoothManager: Bluetooth is powered off") + os_log(.info, log: Logger.bluetooth, "Bluetooth is powered off") case .unauthorized: - print("BluetoothManager: Bluetooth access is unauthorized") + os_log(.error, log: Logger.bluetooth, "Bluetooth access is unauthorized") case .unsupported: - print("BluetoothManager: Bluetooth is not supported") + os_log(.error, log: Logger.bluetooth, "Bluetooth is not supported") default: - print("BluetoothManager: Bluetooth state: \(central.state.rawValue)") + os_log(.debug, log: Logger.bluetooth, "Bluetooth state: %{public}d", central.state.rawValue) } } } diff --git a/KiaMaps/Core/LocalServer/LocalCredentialClient.swift b/KiaMaps/Core/LocalServer/LocalCredentialClient.swift index 45d0fff..a032eed 100644 --- a/KiaMaps/Core/LocalServer/LocalCredentialClient.swift +++ b/KiaMaps/Core/LocalServer/LocalCredentialClient.swift @@ -8,6 +8,7 @@ import Foundation import Network +import os.log enum LocalCredentialClientError: Error { case noCredentials @@ -67,14 +68,14 @@ final class LocalCredentialClient { connection.stateUpdateHandler = { state in switch state { case .ready: - print("LocalCredentialClient: Connected to server") + os_log(.info, log: Logger.server, "Connected to server") case .failed(let error), .waiting(let error): - print("LocalCredentialClient: Connection failed: \(error)") + os_log(.error, log: Logger.server, "Connection failed: %{public}@", error.localizedDescription) completion(.failure(error)) case .cancelled: - print("LocalCredentialClient: Connection cancelled") + os_log(.debug, log: Logger.server, "Connection cancelled") default: - print("LocalCredentialClient: Connection state \(state)") + os_log(.debug, log: Logger.server, "Connection state: %{public}@", String(describing: state)) } } @@ -88,7 +89,7 @@ final class LocalCredentialClient { connection.send(content: requestData, completion: .contentProcessed { error in if let error = error { - print("LocalCredentialClient: Send error: \(error)") + os_log(.error, log: Logger.server, "Send error: %{public}@", error.localizedDescription) completion(.failure(error)) connection.cancel() return @@ -99,7 +100,7 @@ final class LocalCredentialClient { defer { connection.cancel() } if let error = error { - print("LocalCredentialClient: Receive error: \(error)") + os_log(.error, log: Logger.server, "Receive error: %{public}@", error.localizedDescription) completion(.failure(error)) connection.cancel() return @@ -125,14 +126,14 @@ final class LocalCredentialClient { completion(.success(response)) connection.cancel() } catch { - print("LocalCredentialClient: Decode error: \(error)") + os_log(.error, log: Logger.server, "Decode error: %{public}@", error.localizedDescription) completion(.failure(error)) connection.cancel() } } }) } catch { - print("LocalCredentialClient: Encode error: \(error)") + os_log(.error, log: Logger.server, "Encode error: %{public}@", error.localizedDescription) completion(.failure(error)) connection.cancel() } diff --git a/KiaMaps/Core/LocalServer/LocalCredentialServer.swift b/KiaMaps/Core/LocalServer/LocalCredentialServer.swift index 3c8d04f..7a07c1f 100644 --- a/KiaMaps/Core/LocalServer/LocalCredentialServer.swift +++ b/KiaMaps/Core/LocalServer/LocalCredentialServer.swift @@ -8,6 +8,7 @@ import Foundation import Network +import os.log /// Local server that provides credentials to extensions securely final class LocalCredentialServer { @@ -60,7 +61,7 @@ final class LocalCredentialServer { /// Starts the local server func start(completion: ((Bool) -> Void)? = nil) { guard listener == nil else { - print("LocalCredentialServer: Server already running") + os_log(.default, log: Logger.server, "Server already running") completion?(true) return } @@ -74,7 +75,7 @@ final class LocalCredentialServer { do { listener = try NWListener(using: parameters, on: NWEndpoint.Port(integerLiteral: port)) } catch { - print("LocalCredentialServer: Failed to create listener: \(error)") + os_log(.error, log: Logger.server, "Failed to create listener: %{public}@", error.localizedDescription) completion?(false) return } @@ -82,19 +83,19 @@ final class LocalCredentialServer { listener?.stateUpdateHandler = { [weak self] state in switch state { case .ready: - print("LocalCredentialServer: Server is ready on port \(self?.port ?? 0)") + os_log(.info, log: Logger.server, "Server is ready on port %{public}d", self?.port ?? 0) completion?(true) case .failed(let error): - print("LocalCredentialServer: Server failed with error: \(error)") + os_log(.error, log: Logger.server, "Server failed with error: %{public}@", error.localizedDescription) self?.handleServerFailure(error: error) completion?(false) case .cancelled: - print("LocalCredentialServer: Server cancelled") + os_log(.info, log: Logger.server, "Server cancelled") completion?(false) case .waiting(let error): - print("LocalCredentialServer: Server waiting: \(error)") + os_log(.debug, log: Logger.server, "Server waiting: %{public}@", error.localizedDescription) default: - print("LocalCredentialServer: Server state: \(state)") + os_log(.debug, log: Logger.server, "Server state: %{public}@", String(describing: state)) } } @@ -111,7 +112,7 @@ final class LocalCredentialServer { // Retry after a short delay for certain errors if case .posix(let posixError) = error, posixError == .EADDRINUSE { - print("LocalCredentialServer: Port in use, retrying in 5 seconds...") + os_log(.default, log: Logger.server, "Port in use, retrying in 5 seconds...") DispatchQueue.global().asyncAfter(deadline: .now() + 5.0) { [weak self] in self?.start() } @@ -122,7 +123,7 @@ final class LocalCredentialServer { func stop() { listener?.cancel() listener = nil - print("LocalCredentialServer: Server stopped") + os_log(.info, log: Logger.server, "Server stopped") } /// Handles incoming connections @@ -130,13 +131,13 @@ final class LocalCredentialServer { connection.stateUpdateHandler = { state in switch state { case .ready: - print("LocalCredentialServer: Connection ready") + os_log(.debug, log: Logger.server, "Connection ready") case .failed(let error): - print("LocalCredentialServer: Connection failed: \(error)") + os_log(.error, log: Logger.server, "Connection failed: %{public}@", error.localizedDescription) case .cancelled: - print("LocalCredentialServer: Connection cancelled") + os_log(.debug, log: Logger.server, "Connection cancelled") default: - print("LocalCredentialServer: Connection state: \(state)") + os_log(.debug, log: Logger.server, "Connection state: %{public}@", String(describing: state)) } } @@ -147,13 +148,13 @@ final class LocalCredentialServer { guard let self = self else { return } if let error = error { - print("LocalCredentialServer: Receive error: \(error)") + os_log(.error, log: Logger.server, "Receive error: %{public}@", error.localizedDescription) connection.cancel() return } guard let data = data, !data.isEmpty else { - print("LocalCredentialServer: No data received") + os_log(.default, log: Logger.server, "No data received") connection.cancel() return } @@ -173,12 +174,12 @@ final class LocalCredentialServer { // Verify password guard request.password == serverPassword else { - print("LocalCredentialServer: Invalid password from extension: \(request.extensionIdentifier)") + os_log(.default, log: Logger.server, "Invalid password from extension: %{public}@", request.extensionIdentifier) sendErrorResponse(connection: connection, error: "Invalid password") return } - print("LocalCredentialServer: Valid request from extension: \(request.extensionIdentifier)") + os_log(.info, log: Logger.server, "Valid request from extension: %{public}@", request.extensionIdentifier) let credentials = LoginCredentialManager.retrieveCredentials() @@ -195,7 +196,7 @@ final class LocalCredentialServer { sendResponse(connection: connection, data: responseData) } catch { - print("LocalCredentialServer: Failed to process request: \(error)") + os_log(.error, log: Logger.server, "Failed to process request: %{public}@", error.localizedDescription) sendErrorResponse(connection: connection, error: "Invalid request format") } } @@ -204,9 +205,9 @@ final class LocalCredentialServer { private func sendResponse(connection: NWConnection, data: Data) { connection.send(content: data, completion: .contentProcessed { error in if let error = error { - print("LocalCredentialServer: Send error: \(error)") + os_log(.error, log: Logger.server, "Send error: %{public}@", error.localizedDescription) } else { - print("LocalCredentialServer: Response sent successfully") + os_log(.debug, log: Logger.server, "Response sent successfully") } connection.cancel() }) diff --git a/KiaMaps/Core/Logging/Logger.swift b/KiaMaps/Core/Logging/Logger.swift new file mode 100644 index 0000000..a72ae87 --- /dev/null +++ b/KiaMaps/Core/Logging/Logger.swift @@ -0,0 +1,50 @@ +// +// Logger.swift +// KiaMaps +// +// Created by Claude on 11.08.2025. +// Copyright © 2025 Lukas Foldyna. All rights reserved. +// + +import Foundation +import os.log + +/// Centralized logging utility for the KiaMaps application +enum Logger { + + // MARK: - Subsystems + + private static let subsystem = Bundle.main.bundleIdentifier ?? "com.kiamaps" + + // MARK: - Log Categories + + /// API and network related logging + static let api = OSLog(subsystem: subsystem, category: "API") + + /// Authentication and authorization logging + static let auth = OSLog(subsystem: subsystem, category: "Auth") + + /// Local server and credential sharing + static let server = OSLog(subsystem: subsystem, category: "Server") + + /// Application lifecycle and background tasks + static let app = OSLog(subsystem: subsystem, category: "App") + + /// UI and view related logging + static let ui = OSLog(subsystem: subsystem, category: "UI") + + /// Bluetooth connectivity + static let bluetooth = OSLog(subsystem: subsystem, category: "Bluetooth") + + /// Keychain and secure storage + static let keychain = OSLog(subsystem: subsystem, category: "Keychain") + + /// Vehicle data and status + static let vehicle = OSLog(subsystem: subsystem, category: "Vehicle") + + /// Extension and Siri integration + static let `extension` = OSLog(subsystem: subsystem, category: "Extension") + + /// General default logging + static let general = OSLog(subsystem: subsystem, category: "General") +}