diff --git a/mac/Config/Config/AddKeyboardView.swift b/mac/Config/Config/AddKeyboardView.swift index 3a337a667e..d20f9cbfbe 100644 --- a/mac/Config/Config/AddKeyboardView.swift +++ b/mac/Config/Config/AddKeyboardView.swift @@ -9,6 +9,7 @@ import SwiftUI import KeymanSettings +import OSLog struct AddKeyboardView: View { @EnvironmentObject var settings: SettingsContainer @@ -52,7 +53,7 @@ struct AddKeyboardView: View { // Placement determines where on the bar it sits ToolbarItem(placement: .cancellationAction) { Button("Close") { - print("close button clicked") + Logger.app.debug("AddKeyboardView close button clicked") dismissAddKeyboardView() if settings.isInstallationInProgress() { settings.userCanceledPackageInstallation() @@ -61,7 +62,7 @@ struct AddKeyboardView: View { } } .onDisappear { - print("AddKeyboardView onDisappear") + Logger.app.debug("AddKeyboardView onDisappear") downloadCoordinator.cancelActiveDownload() } .alert("Package Installation Failed", isPresented: $downloadCoordinator.loadPackageFailed) { @@ -75,11 +76,11 @@ struct AddKeyboardView: View { if let helper = downloadCoordinator.installHelper { PackageConfirmationView(installHelper: helper) { accepted in if accepted { - print("installing validated package: \(helper.packageName ?? "unknown package")") + Logger.download.info("installing validated package: \(helper.packageName ?? "unknown package", privacy: .public)") do { try settings.installPackage() } catch { - print("failed to install package: \(helper.packageName ?? "unknown package") with error: \(error.localizedDescription)") + Logger.download.error("failed to install package: \(helper.packageName ?? "unknown package") with error: \(error as NSError, privacy: .public)") } } else { settings.userCanceledPackageInstallation() diff --git a/mac/Config/Config/ConfigApp.swift b/mac/Config/Config/ConfigApp.swift index 329ccdcfc7..98606c8439 100644 --- a/mac/Config/Config/ConfigApp.swift +++ b/mac/Config/Config/ConfigApp.swift @@ -12,9 +12,8 @@ import OSLog extension Logger { private static let configSubsystem = ConfigAppUtil.configBundleId - static let package = Logger(subsystem: configSubsystem, category: "package") + static let app = Logger(subsystem: configSubsystem, category: "app") static let download = Logger(subsystem: configSubsystem, category: "download") - static let ui = Logger(subsystem: configSubsystem, category: "ui") } @main @@ -24,7 +23,7 @@ struct ConfigApp: App { @Environment(\.openWindow) private var openWindow init() { - print("tier: \(ConfigAppUtil.appTier)") + Logger.app.log("Starting Keyman Configuration, version: \(ConfigAppUtil.versionWithTag), versionWithTag: \(ConfigAppUtil.versionWithTag)") } var body: some Scene { diff --git a/mac/Config/Config/DownloadCoordinator.swift b/mac/Config/Config/DownloadCoordinator.swift index cde03e62cb..a1b74b0677 100644 --- a/mac/Config/Config/DownloadCoordinator.swift +++ b/mac/Config/Config/DownloadCoordinator.swift @@ -48,7 +48,7 @@ public class DownloadCoordinator: NSObject, ObservableObject, WKNavigationDelega decisionHandler(.cancel) return } - Logger.download.info("received url: \(urlString, privacy: .public)") + Logger.download.log("received url: \(urlString, privacy: .public)") // if the url matches the install url pattern, then cancel the request, // build the standard URLRequest for a package installation and send it @@ -146,7 +146,7 @@ public class DownloadCoordinator: NSObject, ObservableObject, WKNavigationDelega completionHandler(helper.temporaryKmpFileLocation) } } catch { - Logger.download.error("could not initiate package download, error: \(String(describing: error), privacy: .public)") + Logger.download.error("could not initiate package download, error: \(error as NSError, privacy: .public)") self.loadPackageFailed = true self.loadFailureMessage = error.localizedDescription completionHandler(nil) @@ -173,7 +173,7 @@ public class DownloadCoordinator: NSObject, ObservableObject, WKNavigationDelega } public func download(_ download: WKDownload, didFailWithError error: Error, resumeData: Data?) { - Logger.download.error("download failed with error: \(String(describing: error), privacy: .public)") + Logger.download.error("download failed with error: \(error as NSError, privacy: .public)") self.isDownloading = false self.progressObserver = nil self.loadPackageFailed = true diff --git a/mac/Config/Config/InstallDebugView.swift b/mac/Config/Config/InstallDebugView.swift index f6f67dc261..bfc1abf1bc 100644 --- a/mac/Config/Config/InstallDebugView.swift +++ b/mac/Config/Config/InstallDebugView.swift @@ -62,10 +62,7 @@ struct InstallDebugView: View { _ = installation.validateUserHasRestarted() } Button("Set Displayed Complete") { - let beforeDisplayed = installation.getHasDisplayedInstallationComplete() installation.setHasDisplayedInstallationComplete() - let afterDisplayed = installation.getHasDisplayedInstallationComplete() - print("hasDisplayedInstallComplete = \(beforeDisplayed) -> \(afterDisplayed)") } Button("debug") { installation.debug() @@ -76,9 +73,6 @@ struct InstallDebugView: View { Button("Kill Keyman") { _ = installation.killKeymanInputMethod() } - Button("Uninstall") { - installation.uninstall() - } Spacer() } .padding() diff --git a/mac/Config/Config/KeyboardSearchView.swift b/mac/Config/Config/KeyboardSearchView.swift index aae6afa2bb..9a3900da60 100644 --- a/mac/Config/Config/KeyboardSearchView.swift +++ b/mac/Config/Config/KeyboardSearchView.swift @@ -12,6 +12,7 @@ import SwiftUI import Combine import WebKit import KeymanSettings +import OSLog struct KeyboardSearchView: NSViewRepresentable { @ObservedObject var coordinator: DownloadCoordinator @@ -22,7 +23,8 @@ struct KeyboardSearchView: NSViewRepresentable { /** Creates the underlying NSView (WKWebView) for macOS */ func makeNSView(context: Context) -> WKWebView { - print("makeNSView called") + Logger.app.debug("KeyboardSearchView makeNSView called") + let webView = WKWebView() // assign the coordinator as the navigation delegate @@ -41,7 +43,7 @@ struct KeyboardSearchView: NSViewRepresentable { func updateNSView(_ nsView: WKWebView, context: Context) { if coordinator.settings == nil { coordinator.settings = self.settings - print("updateNSView, settings intialized for coordinator") + Logger.app.debug("KeyboardSearchView updateNSView, settings intialized for coordinator") } } } diff --git a/mac/Config/Config/MainConfigView.swift b/mac/Config/Config/MainConfigView.swift index d7266bf212..62398cfb31 100644 --- a/mac/Config/Config/MainConfigView.swift +++ b/mac/Config/Config/MainConfigView.swift @@ -8,6 +8,7 @@ import SwiftUI import KeymanSettings +import OSLog struct MainConfigView: View { @@ -96,7 +97,8 @@ struct MainConfigView: View { ) { Button("Delete", role: .destructive) { if let uuid = idToDelete { - print("deleting package.id: \(uuid)") + Logger.app.info("deleting package.id: \(uuid)") + // use multiple expanded states? //expandedStates.removeValue(forKey: uuid) @@ -156,13 +158,13 @@ struct MainConfigView: View { packageInstallHelper = nil if accepted { - print("installing validated package: \(helper.packageName ?? "unknown package")") + Logger.app.info("installing validated package: \(helper.packageName ?? "unknown package", privacy: .public)") do { try settings.installPackage() } catch { self.alertMessage = error.localizedDescription self.isShowingDropKmpAlert = true - print("failed to install package: \(helper.packageName ?? "unknown package") with error: \(error.localizedDescription)") + Logger.app.error("failed to install package: \(helper.packageName ?? "unknown package", privacy: .public), error: \(error as NSError, privacy: .public)") } } else { settings.userCanceledPackageInstallation() diff --git a/mac/Config/Installation/InstallationCheck.swift b/mac/Config/Installation/InstallationCheck.swift index 283792d004..0d153bb732 100644 --- a/mac/Config/Installation/InstallationCheck.swift +++ b/mac/Config/Installation/InstallationCheck.swift @@ -14,8 +14,9 @@ import Foundation import KeymanSettings +import OSLog -public enum InstallationPhase { +public enum InstallationPhase: String { case inputMethodMissing case inputMethodOutdated case evaluatingInstallation @@ -37,7 +38,7 @@ public enum InstallationPhase { } } -enum InstallationStateCondition { +enum InstallationStateCondition: String { case stale case new case inProgress @@ -112,8 +113,8 @@ public class InstallationCheck { // the input method is valid, examine the installation state recorded on disk // let installationStateCondition = InstallationCheck.evaluateInstallationState(state: installState, for: keymanVersion); - print("installationStateCondition: \(installationStateCondition)") - + Logger.app.log("installationStateCondition: \(installationStateCondition.rawValue, privacy: .public)") + switch installationStateCondition { case .inProgress: self.installationState = installState // resume with the existing installation @@ -181,7 +182,7 @@ public class InstallationCheck { * for testing purposes, by specifying `kTestConfigVersion` in config app's standard UserDefaults */ static func isVersionCurrent(inputMethodVersion: String, configurationVersion: String) -> Bool { - print("isVersionCurrent, comparing input method version: \(inputMethodVersion) and config app version: \(configurationVersion)") + Logger.app.log("isVersionCurrent, comparing input method version: \(inputMethodVersion, privacy: .public) and config app version: \(configurationVersion, privacy: .public)") return inputMethodVersion == configurationVersion } @@ -190,7 +191,8 @@ public class InstallationCheck { * checks the current state of Accessibility permissions */ func registerObservers() { - print("InstallationCheck registerObservers") + Logger.app.debug("InstallationCheck registerObservers") + DistributedNotificationCenter.default().addObserver( self, selector: #selector(self.handleAccessibilityResponse(_:)), @@ -206,11 +208,12 @@ public class InstallationCheck { @objc func handleAccessibilityResponse(_ notification: Notification) { var installCompleted = false - print("handleAccessibilityResponse") // Extract message from the notification if available if let message = notification.object as? String { let permissionGranted = self.processAccessibilityResponse(with: message) + Logger.app.debug("handleAccessibilityResponse, message: \(message, privacy: .public)") + if let state = self.installationState { installCompleted = state.isComplete } @@ -231,7 +234,7 @@ public class InstallationCheck { } } } else { - print("accessibilityStateResponse received but did not include message") + Logger.app.debug("handleAccessibilityResponse, received but did not include message") } } @@ -244,8 +247,8 @@ public class InstallationCheck { .minute(.twoDigits) .second(.twoDigits) .secondFraction(.fractional(3)) - print("processAccessibilityResponse received message: \(message), time: \(Date().formatted(timeStyle))") - + Logger.app.debug("processAccessibilityResponse received message: \(message, privacy: .public), time: \(Date().formatted(timeStyle), privacy: .public)") + // if the message indicates that access was granted, then return true return !message.isEmpty && message == kAccessibilityPermissionGrantedMessage } @@ -313,8 +316,9 @@ public class InstallationCheck { * Creates a InstallationState object describing a new installation */ func createNewInstallationState(with neededTasks: Set) -> InstallationState { - print("completeNewInstallationEvaluation: created new installation state") - var fullTaskList = neededTasks + Logger.app.debug("completeNewInstallationEvaluation: created new installation state") + + var fullTaskList = neededTasks // add prepareNewInstall InstallationTask fullTaskList.insert(InstallationTask.createNewInstallationTask(type: .prepareNewInstall)) @@ -331,10 +335,10 @@ public class InstallationCheck { func checkForRepair(accessibilityPermissionGranted: Bool) { // check whether the installation requires repair if let state = self.createRepairInstallationState(accessibilityPermissionGranted: accessibilityPermissionGranted) { - print("checkForRepair completed: repair is required") + Logger.app.log("checkForRepair completed: repair is required") self.applyRepairedInstallationState(state: state) } else { - print("checkForRepair completed: no repair needed") + Logger.app.log("checkForRepair completed: no repair needed") } } diff --git a/mac/Config/Installation/InstallationContainer.swift b/mac/Config/Installation/InstallationContainer.swift index 38dbdd665c..6dbb87e502 100644 --- a/mac/Config/Installation/InstallationContainer.swift +++ b/mac/Config/Installation/InstallationContainer.swift @@ -9,6 +9,7 @@ import SwiftUI import Combine import KeymanSettings +import OSLog // in-app notifications sent public extension Notification.Name { @@ -40,10 +41,12 @@ public class InstallationContainer : ObservableObject { // create the settings repository, gaining access to the app group UserDefaults do { defaultsRepo = try DefaultsRepository(suiteName: InputMethodUtil.groupId) - print("Found group container") + Logger.app.log("found group container") } catch UserDefaultsError.unknownSuite { + Logger.app.error("group container not found: \(UserDefaultsError.unknownSuite)") fatalError("Group container not found.") } catch { + Logger.app.error("unable to access settings in group container: \(error as NSError, privacy: .public)") fatalError("Unable to access settings in group container.") } @@ -70,7 +73,8 @@ public class InstallationContainer : ObservableObject { * register observers to learn of results of InstallationState evaluation */ func registerObservers() { - print("InstallationContainer registerObservers") + Logger.app.debug("InstallationContainer registerObservers") + NotificationCenter.default.addObserver( self, selector: #selector(self.handleStartNewInstallation(_:)), @@ -101,7 +105,8 @@ public class InstallationContainer : ObservableObject { * called when `NSNotification.Name.startNewInstallation` is received */ @objc func handleStartNewInstallation(_ notification: Notification) { - print("handleStartNewInstallation received") + Logger.app.debug("handleStartNewInstallation received") + // the evaluation is done self.installationCheck.isEvaluatingNewInstallation = false } @@ -110,7 +115,7 @@ public class InstallationContainer : ObservableObject { * called when `NSNotification.Name.startInstallationRepair` is received */ @objc func handleStartInstallationRepair(_ notification: Notification) { - print("handleStartInstallationRepair received") + Logger.app.debug("handleStartInstallationRepair received") // notify observers NotificationCenter.default.post(name: .installationRepairStarted, object: nil, userInfo: nil) @@ -180,7 +185,7 @@ public class InstallationContainer : ObservableObject { public func currentTask() -> InstallationTask? { guard let state = self.installationState else { return nil } guard self.installationPhase.hasTasks else { - print("the installation phase \(self.installationPhase) has no tasks"); + Logger.app.error("the installation phase \(self.installationPhase.rawValue, privacy: .public) has no tasks") return nil } @@ -211,7 +216,7 @@ public class InstallationContainer : ObservableObject { func executeTask(_ task: InstallationTask) { guard self.installationState != nil else { return } guard self.installationPhase.hasTasks else { - print("the installation phase \(self.installationPhase) has no tasks"); + Logger.app.error("executeTask: the installation phase \(self.installationPhase.rawValue) has no tasks") return } @@ -247,7 +252,7 @@ public class InstallationContainer : ObservableObject { * the property in InstallationCheck with the new reference. */ public func updateTaskAsCompleted(taskType: InstallationTaskType) { - print("executeTask: \(taskType.rawValue) completed") + Logger.app.debug("executeTask: \(taskType.rawValue, privacy: .public) completed") if let existingState = self.installationState { let updatedState = InstallationState.createCopyWithCompletedTask(from: existingState, with: taskType) self.installationCheck.installationState = updatedState @@ -269,8 +274,8 @@ public class InstallationContainer : ObservableObject { */ public func migrateData() -> Bool { let success = self.inputMethodUtil.invokeKeymanInputMethodMigration() - print("migration suceeded: \(success)") - + Logger.app.debug("migration suceeded: \(success)") + // check whether if success { NotificationCenter.default.post(name: .dataMigrated, object: nil) @@ -348,10 +353,10 @@ public class InstallationContainer : ObservableObject { if let timeRestartRequested = state.dateRestartRequested { if let mostRecentStartupTime = self.getMostRecentRestartTime() { hasRestarted = mostRecentStartupTime > timeRestartRequested - print("mostRecentStartupTime: \(mostRecentStartupTime), timeRestartRequested: \(timeRestartRequested)") + Logger.app.debug("mostRecentStartupTime: \(mostRecentStartupTime), timeRestartRequested: \(timeRestartRequested)") } } - print("validateRestarted: \(hasRestarted)") + Logger.app.debug("validateRestarted: \(hasRestarted)") return hasRestarted } @@ -386,7 +391,7 @@ public class InstallationContainer : ObservableObject { let enabled = inputMethodUtil.isKeymanInputMethodEnabled() let running = inputMethodUtil.isKeymanInputMethodRunning() - print("Keyman status, version: \(version), enabled: \(enabled), running: \(running), permissionGranted: \(permissionString)") + Logger.app.debug("Keyman status, version: \(version, privacy: .private), enabled: \(enabled), running: \(running), permissionGranted: \(permissionString)") } /** @@ -394,7 +399,7 @@ public class InstallationContainer : ObservableObject { */ public func registerKeymanInputMethod() -> Bool { let success = self.inputMethodUtil.registerKeymanInputMethod() - print("registerKeymanInputMethod suceeded: \(success)") + Logger.app.debug("registerKeymanInputMethod suceeded: \(success)") return success } @@ -404,8 +409,8 @@ public class InstallationContainer : ObservableObject { */ public func selectKeymanInputMethod() -> Bool { let success = self.inputMethodUtil.selectKeymanInputMethod() - print("selectKeymanInputMethod suceeded: \(success)") - + Logger.app.debug("selectKeymanInputMethod suceeded: \(success)") + return success } @@ -426,7 +431,7 @@ public class InstallationContainer : ObservableObject { success = self.inputMethodUtil.enableKeymanInputMethod() } - print("enableKeymanInputMethod suceeded: \(success)") + Logger.app.debug("enableKeymanInputMethod suceeded: \(success)") return success } @@ -445,8 +450,8 @@ public class InstallationContainer : ObservableObject { var requested = false requested = self.inputMethodUtil.invokeKeymanInputMethodRequestAccess() - print("requestAccessibility called, requested: \(requested)") - + Logger.app.debug("requestAccessibility called, requested: \(requested)") + return requested } @@ -463,12 +468,4 @@ public class InstallationContainer : ObservableObject { public func disableKeymanInputMethod() -> Bool { return self.inputMethodUtil.disableKeymanInputMethod() } - - /** - * uninstall the Keyman Input Method - * not functional with default security settings! - */ - public func uninstall() { - self.inputMethodUtil.uninstallKeyman() - } } diff --git a/mac/KeymanSettings/Sources/KeymanSettings/ConfigAppUtil.swift b/mac/KeymanSettings/Sources/KeymanSettings/ConfigAppUtil.swift index 597061e251..b840d1583a 100644 --- a/mac/KeymanSettings/Sources/KeymanSettings/ConfigAppUtil.swift +++ b/mac/KeymanSettings/Sources/KeymanSettings/ConfigAppUtil.swift @@ -11,7 +11,8 @@ import OSLog extension Logger { private static let settingsSubsystem = "com.keyman.settings" - static let settings = Logger(subsystem: settingsSubsystem, category: "settings") + static let setup = Logger(subsystem: settingsSubsystem, category: "setup") + static let data = Logger(subsystem: settingsSubsystem, category: "data") } public struct ConfigAppUtil { diff --git a/mac/KeymanSettings/Sources/KeymanSettings/DefaultsRepo.swift b/mac/KeymanSettings/Sources/KeymanSettings/DefaultsRepo.swift index 6c1f9e3cd6..f467f51c92 100644 --- a/mac/KeymanSettings/Sources/KeymanSettings/DefaultsRepo.swift +++ b/mac/KeymanSettings/Sources/KeymanSettings/DefaultsRepo.swift @@ -18,6 +18,5 @@ public protocol DefaultsRepo { func writeEnabledKeyboards(enabledKeyboardsArray: [String]) func readSelectedKeyboard() -> String func writeSelectedKeyboard(keyboardName: String) - func logDefaults() func clearDefaults() } diff --git a/mac/KeymanSettings/Sources/KeymanSettings/InputMethodUtil.swift b/mac/KeymanSettings/Sources/KeymanSettings/InputMethodUtil.swift index 43dc62c170..285a823e3f 100644 --- a/mac/KeymanSettings/Sources/KeymanSettings/InputMethodUtil.swift +++ b/mac/KeymanSettings/Sources/KeymanSettings/InputMethodUtil.swift @@ -10,6 +10,7 @@ import Foundation import Carbon.HIToolbox import AppKit +import OSLog public enum KeymanVersionCheckError: Error { case inputMethodNotFound @@ -51,7 +52,7 @@ public class InputMethodUtil { */ public func keymanInputMethodExists() -> Bool { guard let inputMethodUrl = pathUtil.buildInputMethodPathUrl(fileName: self.keymanInputMethodApplicationName) else { - print("Keyman input method not found, failed to create input method url") + Logger.setup.log("Keyman input method not found, failed to create input method url") return false } @@ -117,13 +118,13 @@ public class InputMethodUtil { /** * uninstalls the Keyman input method - * note: not useful to expose to users as default security systems prevent us from deleting the app + * note: commenting out for now as default security settings prevent us from deleting the app */ - public func uninstallKeyman() { - _ = self.killKeymanInputMethod() - _ = self.disableKeymanInputMethod() - self.deleteKeyman() - } +// public func uninstallKeyman() { +// _ = self.killKeymanInputMethod() +// _ = self.disableKeymanInputMethod() +// self.deleteKeyman() +// } /** * Returns version number string for the specifed app located at `~/Library/Input Methods` @@ -144,7 +145,7 @@ public class InputMethodUtil { guard let appVersionString = infoDictionary["CFBundleShortVersionString"] as? String else { throw KeymanVersionCheckError.versionNotFound } - + return appVersionString } @@ -167,15 +168,13 @@ public class InputMethodUtil { } public func invokeKeymanInputMethodMigration() -> Bool { - print("invokeKeymanInputMethodMigration()") + Logger.setup.log("invokeKeymanInputMethodMigration()") return self.invokeKeymanInputMethodAsSubProcess(argument: kMigrateCommand) == 0 } public func invokeKeymanInputMethodRequestAccess() -> Bool { var success = false do { - print("invokeKeymanInputMethodRequestAccess()") - // because we are launching Keyman with a specific command line argument // for this request, we must kill it first _ = self.killKeymanInputMethod() @@ -183,7 +182,7 @@ public class InputMethodUtil { try self.launchKeymanInputMethodAsSeparateProcess(argument: kAccessCommand) success = true } catch { - print("error requesting access: \(error)") + Logger.setup.error("error requesting Accessibility from input method: \(error as NSError, privacy: .public)") } return success @@ -197,8 +196,8 @@ public class InputMethodUtil { * It contains a message with a value of `granted` or `not-granted` */ func invokeKeymanInputMethodCheckAccess() throws { - print("invokeKeymanInputMethodCheckAccess()") - + Logger.setup.info("invokeKeymanInputMethodCheckAccess()") + // because we are launching Keyman with a specific command line argument // for this request, we must kill it first _ = self.killKeymanInputMethod() @@ -214,13 +213,12 @@ public class InputMethodUtil { let process = Process() if let executableUrl = self.pathUtil.buildInputMethodExecutableUrl(fileName: self.keymanInputMethodApplicationName) { process.executableURL = executableUrl - print("invoking Keyman at: \(String(describing: process.executableURL))") + Logger.setup.info("invoking Keyman at: \(String(describing: process.executableURL), privacy: .public)") process.arguments = [argument] } var currentEnv = ProcessInfo.processInfo.environment - print("current env: \(String(describing: currentEnv))") - + currentEnv["__CFBundleIdentifier"] = InputMethodUtil.keymanBundleId // set bundle ID to that of the Keyman input method process.environment = currentEnv @@ -229,10 +227,9 @@ public class InputMethodUtil { process.waitUntilExit() // wait for it to finish result = Int(process.terminationStatus) } catch { - print("Failed to run process: \(error)") + Logger.setup.error("Failed to run process: \(error as NSError, privacy: .public)") } - print("invokeKeymanInputMethod() result: \(result)") return result } @@ -246,16 +243,13 @@ public class InputMethodUtil { } guard let inputMethodUrl = pathUtil.buildInputMethodPathUrl(fileName: self.keymanInputMethodApplicationName) else { - print("launchKeymanInputMethodAsSeparateProcess, failed to create input method url") + Logger.setup.error("launchKeymanInputMethodAsSeparateProcess, failed to create input method url") throw KeymanInvocationError.inputMethodNotFound } NSWorkspace.shared.openApplication(at: inputMethodUrl, configuration: openConfig) { (app, error) in if let error = error { - print("Could not launch Keyman input method at \(inputMethodUrl), due to error: \(error.localizedDescription), code: \(error._code)") - Thread.callStackSymbols.forEach { symbol in - print(symbol) - } + Logger.setup.error("Could not launch Keyman input method at \(inputMethodUrl), due to error: \(error as NSError, privacy: .public)") } } } @@ -268,7 +262,7 @@ public class InputMethodUtil { do { try self.invokeKeymanInputMethodCheckAccess() } catch { - print("invoking Keyman failed: \(error.localizedDescription)") + Logger.setup.error("invoking Keyman failed: \(error as NSError, privacy: .public)") } let timeStyle = Date.FormatStyle() @@ -276,7 +270,7 @@ public class InputMethodUtil { .minute(.twoDigits) .second(.twoDigits) .secondFraction(.fractional(3)) - print("doAsyncAccessibilityCheck, listening across process boundaries, time: \(Date().formatted(timeStyle))") + Logger.setup.log("doAsyncAccessibilityCheck, listening across process boundaries, time: \(Date().formatted(timeStyle))") } /** @@ -287,11 +281,11 @@ public class InputMethodUtil { let runningApps = NSRunningApplication.runningApplications(withBundleIdentifier: bundleId) var didTerminate = false - print("Running app count for \(bundleId) = \(runningApps.count)") + Logger.setup.debug("Running app count for \(bundleId, privacy: .public) = \(runningApps.count)") if let runningApp = runningApps.first { let processId = runningApp.processIdentifier didTerminate = runningApp.terminate() - print("process \(processId) for \(bundleId) was terminated: \(didTerminate)") + Logger.setup.log("process \(processId) for \(bundleId, privacy: .public) was terminated: \(didTerminate)") } return didTerminate @@ -314,8 +308,8 @@ public class InputMethodUtil { let inputSourceList = TISCreateInputSourceList(properties as CFDictionary, true) guard let sources = inputSourceList?.takeRetainedValue() as? [TISInputSource], let targetSource = sources.first else { - print("Error: Could not find the specified input source.") - return(nil) + Logger.setup.error("Could not find the specified input source with bundleID: \(bundleId, privacy: .public)") + return(nil) } return targetSource @@ -333,44 +327,19 @@ public class InputMethodUtil { // Bridge the CFTypeRef to an Unmanaged and then to a Swift String if let inputMethodEnabled = Unmanaged.fromOpaque(cfType).takeUnretainedValue() as? Bool { enabled = inputMethodEnabled - print("is enabled: \(enabled)") + Logger.setup.info("isInputMethodEnabled: \(enabled)") } else { - print("could not read retrieved enabled property for bundleId: \(bundleId)") + Logger.setup.error("Could not read retrieved enabled property for bundleId: \(bundleId, privacy: .public)") } } else { - print("Failed to get enabled property for bundleId: \(bundleId)") + Logger.setup.error("Failed to get enabled property for bundleId: \(bundleId, privacy: .public)") } } else { - print("Failed to get input source for bundleId: \(bundleId)") + Logger.setup.error("Failed to get input source for bundleId: \(bundleId, privacy: .public)") } return enabled } - /** - * returns true if the input method with the specified bundleId is capable of being enabled - */ - func isInputMethodEnableCapable(bundleId: String) -> Bool { - var enableCapable = false - - if let inputSource = self.getInputSource(bundleId: bundleId) { - let enableCapableValue = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsEnableCapable) - if let cfType = enableCapableValue { - // Bridge the CFTypeRef to an Unmanaged and then to a Swift String - if let capable = Unmanaged.fromOpaque(cfType).takeUnretainedValue() as? Bool { - enableCapable = capable - print("is enable capable: \(enableCapable)") - } else { - print("could not read retrieved enable capable property for bundleId: \(bundleId)") - } - } else { - print("Failed to get enable capable property for bundleId: \(bundleId)") - } - } else { - print("Failed to get input source for bundleId: \(bundleId)") - } - return enableCapable - } - /** * register the newly installed input method with the specified bundleId * this will allow a `TISInputSourceRef` to be obtained to access the input source @@ -379,7 +348,7 @@ public class InputMethodUtil { var success = false guard let inputMethodUrl = pathUtil.buildInputMethodPathUrl(fileName: self.keymanInputMethodApplicationName) else { - print("registerInputMethod, failed to create input method url") + Logger.setup.error("registerInputMethod, failed to create input method url for bundleId: \(bundleId, privacy: .public)") return false } let cfUrl = inputMethodUrl as CFURL @@ -388,9 +357,9 @@ public class InputMethodUtil { success = result == noErr if (success) { - print("registerInputMethod for bundle ID '\(bundleId)': success") + Logger.setup.log("registerInputMethod for bundle ID '\(bundleId, privacy: .public)': success") } else { - print("registerInputMethod for bundle ID '\(bundleId)' failed, result = \(result)") + Logger.setup.error("registerInputMethod for bundle ID '\(bundleId, privacy: .public)' failed, result = \(result)") } return success @@ -405,9 +374,9 @@ public class InputMethodUtil { let result = TISEnableInputSource(inputSource) success = result == noErr if (success) { - print("enableInputMethod for bundle ID '\(bundleId)': success") + Logger.setup.log("enableInputMethod for bundle ID '\(bundleId, privacy: .public)': success") } else { - print("enableInputMethod for bundle ID '\(bundleId)' failed, result = \(result)") + Logger.setup.error("enableInputMethod for bundle ID '\(bundleId, privacy: .public)' failed, result = \(result)") } } return success @@ -422,27 +391,31 @@ public class InputMethodUtil { let result = TISDisableInputSource(inputSource) success = result == noErr if (success) { - print("disableInputMethod for bundle ID '\(bundleId)': success") + Logger.setup.log("disableInputMethod for bundle ID '\(bundleId, privacy: .public)': success") } else { - print("disableInputMethod for bundle ID '\(bundleId)' failed, result = \(result)") + Logger.setup.error("disableInputMethod for bundle ID '\(bundleId, privacy: .public)' failed, result = \(result)") } } return success } - func deleteKeyman() { - let fileManager = FileManager.default - if let keymanFile = self.pathUtil.buildInputMethodPathUrl(fileName: keymanInputMethodApplicationName) { - do { - try fileManager.removeItem(at: keymanFile) - print("Successfully deleted Keyman.app") - } catch { - print("Error deleting Keyman.app: \(error)") - } - } else { - print("Keyman.app not found") - } - } + /** + * deletes the Keyman input method + * note: commenting out for now as default security settings prevent us from deleting the app + */ +// func deleteKeyman() { +// let fileManager = FileManager.default +// if let keymanFile = self.pathUtil.buildInputMethodPathUrl(fileName: keymanInputMethodApplicationName) { +// do { +// try fileManager.removeItem(at: keymanFile) +// print("Successfully deleted Keyman.app") +// } catch { +// print("Error deleting Keyman.app: \(error)") +// } +// } else { +// print("Keyman.app not found") +// } +// } /** * select the input source with the specified input source id and return true if successful @@ -454,16 +427,16 @@ public class InputMethodUtil { let inputSourceList = TISCreateInputSourceList(properties as CFDictionary, false) guard let sources = inputSourceList?.takeRetainedValue() as? [TISInputSource], let targetSource = sources.first else { - print("Error: Could not find the input source '\(inputSourceId)'.") + Logger.setup.error("Error: Could not find the input source '\(inputSourceId, privacy: .public)'.") return false } let result = TISSelectInputSource(targetSource) if result != noErr { - print("Error selecting input source '\(inputSourceId)': \(result)") - return false + Logger.setup.error("Error selecting input source '\(inputSourceId, privacy: .public)'.") + return false } else { - print("Successfully selected input source '\(inputSourceId)'.") + Logger.setup.log("Successfully selected input source '\(inputSourceId, privacy: .public)'.") return true } } diff --git a/mac/KeymanSettings/Sources/KeymanSettings/SettingsContainer.swift b/mac/KeymanSettings/Sources/KeymanSettings/SettingsContainer.swift index f4932bedd2..aacdbf03f7 100644 --- a/mac/KeymanSettings/Sources/KeymanSettings/SettingsContainer.swift +++ b/mac/KeymanSettings/Sources/KeymanSettings/SettingsContainer.swift @@ -24,6 +24,7 @@ import Foundation import Combine import ZIPFoundation +import OSLog public enum InstallPackageError: LocalizedError { case packageInstallationAlreadyInProgress @@ -116,7 +117,7 @@ public class SettingsContainer : ObservableObject { let languageQueryItem = URLQueryItem(name:"lang", value: languageString) searchUrl.append(queryItems:[languageQueryItem]) } - print("full searchUrl: \(searchUrl.absoluteString)") + Logger.setup.info("full searchUrl: \(searchUrl.absoluteString, privacy: .public)") return searchUrl } @@ -131,7 +132,7 @@ public class SettingsContainer : ObservableObject { var primaryLanguage: String? if let language = systemLanguages.first { primaryLanguage = language - print("primary system language: \(String(describing: primaryLanguage))") + Logger.setup.info("primary system language: \(language, privacy: .public)") } return primaryLanguage @@ -146,20 +147,24 @@ public class SettingsContainer : ObservableObject { // create the package repository, gaining access to the app group container directory do { try self.packageRepository = PackageRepository() - print("Found documents group container") + Logger.data.log("Found documents group container") } catch KeymanPathError.groupContainerNotFound { + Logger.data.error("Document group container not found") fatalError("Document group container not found.") } catch { + Logger.data.error("Unable to access documents in group container, error \(error as NSError, privacy: .public)") fatalError("Unable to access documents in group container.") } // create the settings repository, gaining access to the app group UserDefaults do { try self.defaultsRepository = DefaultsRepository(suiteName: InputMethodUtil.groupId) - print("Found defaults group container") + Logger.data.log("Found defaults group container") } catch UserDefaultsError.unknownSuite { + Logger.data.error("Defaults group container not found") fatalError("Defaults group container not found.") } catch { + Logger.data.error("Unable to access defaults in group container, error \(error as NSError, privacy: .public)") fatalError("Unable to access defaults in group container: \(error.localizedDescription).") } @@ -246,7 +251,7 @@ public class SettingsContainer : ObservableObject { * Called when user chooses to cancel downgrade of package */ public func userCanceledPackageInstallation() { - print("user cancelled package installation") + Logger.data.log("User cancelled package installation") self.packageInstall?.cleanupFailedInstallation() self.packageInstall = nil @@ -256,7 +261,7 @@ public class SettingsContainer : ObservableObject { * Called when user chooses to cancel downgrade of package */ public func packageInstallationFailed() { - print("packageInstallationFailed") + Logger.data.log("Package installation failed") self.packageInstall?.cleanupFailedInstallation() self.packageInstall = nil @@ -284,7 +289,7 @@ public class SettingsContainer : ObservableObject { */ public func findInstalledPackage(with id: UUID) -> KeymanPackage? { guard let package = self.installedPackages.first(where: { $0.id == id }) else { - print ("Error: could not find package with UUID: \(id)") + Logger.setup.error("error: could not find package with UUID: \(id)") return nil } @@ -297,8 +302,6 @@ public class SettingsContainer : ObservableObject { public func removeInstalledPackage(with id: UUID) { if let package = findInstalledPackage(with: id) { self.removeInstalledPackage(package: package) - } else { - print("could not find package with id: \(id)") } } @@ -329,7 +332,7 @@ public class SettingsContainer : ObservableObject { */ public func isKeyboardEnabled(packageId: UUID, keyboardKey: String) -> Bool { guard let package = self.findInstalledPackage(with: packageId) else { - print ("Could not read keyboard state for package: \(packageId) and keyboard: \(keyboardKey)") + Logger.setup.error("isKeyboardEnabled, not read keyboard state for package: \(packageId) and keyboard: \(keyboardKey, privacy: .public)") return false } @@ -342,11 +345,11 @@ public class SettingsContainer : ObservableObject { */ public func setKeyboardEnabled(packageId: UUID, keyboardKey: String, enabled: Bool) { guard let package = self.findInstalledPackage(with: packageId) else { - print ("Could not read keyboard state for package: \(packageId) and keyboard: \(keyboardKey)") + Logger.setup.error("setKeyboardEnabled, could not read keyboard state for package: \(packageId) and keyboard: \(keyboardKey, privacy: .public)") return } - print ("setKeyboardEnabled for \(keyboardKey) setting to \(enabled)") + Logger.setup.info("setKeyboardEnabled for \(keyboardKey, privacy: .public) setting to \(enabled)") package.enableKeyboard(keyboardKey: keyboardKey, enabled: enabled) // update persisted state in UserDefaults enabledKeyboards array @@ -410,9 +413,9 @@ public class SettingsContainer : ObservableObject { let enabledKeyboardKeys = self.defaultsRepository.readEnabledKeyboards() if (enabledKeyboardKeys.isSubset(of: installedKeyboardKeys)) { - print("only installed keyboards are listed as enabled: no need to update defaults") + Logger.setup.info("only installed keyboards are listed as enabled: no need to update defaults") } else { - print("enabled keyboards list contains uninstalled keyboards: align with enabled keyboards list") + Logger.setup.info("enabled keyboards list contains uninstalled keyboards: align with enabled keyboards list") let installedEnabledKeyboardKeys = enabledKeyboardKeys.intersection(installedKeyboardKeys) self.defaultsRepository.writeEnabledKeyboards(enabledKeyboardsArray: Array(installedEnabledKeyboardKeys)) } @@ -472,7 +475,7 @@ public class SettingsContainer : ObservableObject { * Delegates to the PackageInstallHelper instance to decide whether the package should be installed. */ public func packageDownloadComplete(kmpFileUrl: URL) throws { - print ("packageDownloadComplete \(kmpFileUrl)") + Logger.setup.info("packageDownloadComplete \(kmpFileUrl, privacy: .public)") do { try self.packageInstall?.prepareToInstall(for: kmpFileUrl) @@ -505,7 +508,7 @@ public class SettingsContainer : ObservableObject { self.installedPackages[index] = package self.addEnabledKeyboards(for: package) } else { - print("Error: package '\(package.packageName)' not found for replacement") + Logger.setup.error("Error: package '\(package.packageName, privacy: .public)' not found for replacement") } } } diff --git a/mac/KeymanSettings/Sources/Model/Keyboard.swift b/mac/KeymanSettings/Sources/Model/Keyboard.swift index b368fd4b0f..2fe500f94e 100644 --- a/mac/KeymanSettings/Sources/Model/Keyboard.swift +++ b/mac/KeymanSettings/Sources/Model/Keyboard.swift @@ -10,6 +10,7 @@ import Foundation import AppKit +import OSLog public class Keyboard: Identifiable, Hashable, Equatable { @@ -86,7 +87,7 @@ public class Keyboard: Identifiable, Hashable, Equatable { public func validateKmxFile(in packageDirectory: URL) throws { let kmxFilePath = self.deriveKmxFileUrl(from: packageDirectory).path if !FileManager.default.fileExists(atPath: kmxFilePath) { - print("** error: could not find kmx file \(kmxFilePath)") + Logger.data.error("error: could not find kmx file \(kmxFilePath, privacy: .public)") throw LoadPackageError.missingKmxFile } } diff --git a/mac/KeymanSettings/Sources/Model/KeymanPackage.swift b/mac/KeymanSettings/Sources/Model/KeymanPackage.swift index 3387ff3ac3..a7b548a25f 100644 --- a/mac/KeymanSettings/Sources/Model/KeymanPackage.swift +++ b/mac/KeymanSettings/Sources/Model/KeymanPackage.swift @@ -12,6 +12,7 @@ import AppKit import Cocoa import CoreImage import CoreImage.CIFilterBuiltins +import OSLog public class KeymanPackage: Identifiable, Hashable, Equatable { static let defaultImage: NSImage? = { @@ -19,7 +20,7 @@ public class KeymanPackage: Identifiable, Hashable, Equatable { if let imageUrl = Bundle.module.url(forResource: "SideImage", withExtension: "bmp") { image = NSImage(contentsOf: imageUrl) } else { - print("Error: Could not find SideImage.bmp in the module bundle.") + Logger.setup.error("error: could not find SideImage.bmp in the module bundle") } return image }() @@ -231,7 +232,7 @@ public class KeymanPackage: Identifiable, Hashable, Equatable { if comparisonResult == .orderedAscending { // keyman version is too old meetsRequiredVersion = false - print("for package '\(self.packageName)' keyman version \(keymanVersion) is older than required version \(minimumKeymanVersion)") + Logger.data.log("validateKeymanVersionForPackage for package: '\(self.packageName, privacy: .public)' keyman version \(keymanVersion, privacy: .public) is older than required version \(minimumKeymanVersion, privacy: .public)") } else { meetsRequiredVersion = true } diff --git a/mac/KeymanSettings/Sources/Persistence/DefaultsRepository.swift b/mac/KeymanSettings/Sources/Persistence/DefaultsRepository.swift index e7e8af18ab..f1c0639fc1 100644 --- a/mac/KeymanSettings/Sources/Persistence/DefaultsRepository.swift +++ b/mac/KeymanSettings/Sources/Persistence/DefaultsRepository.swift @@ -151,22 +151,6 @@ public class DefaultsRepository: DefaultsRepo { } } - /** - * for debugging: prints UserDefaults values to the console - * with app group UserDefaults, there is no way to view from the command line - * (unlike standard application-level UserDefaults) - */ - public func logDefaults() { - print("UserDefaults:") - print("\(kSelectedKeyboardKey): \(self.readSelectedKeyboard())") - print("\(kDataModelVersionKey): \(self.readDataModelVersion())") - print("\(kForceSentryErrorKey): \(self.readForceSentryError())") - print("\(kShowOskOnActivateKey): \(self.readShowOskOnActivate())") - print("\(kEnabledKeyboardsKey): \(self.readEnabledKeyboards())") - print("\(kPersistedOptionsKey): \(self.readPersistedOptions())") - print("\(kInstallationState): \(self.readInstallationState()?.description ?? "nil")") - } - /** * for debugging: clear all the entries for the app group UserDefaults * unlike standard application-level UserDefaults, there is no way to view from the command line diff --git a/mac/KeymanSettings/Sources/Persistence/KeymanPaths.swift b/mac/KeymanSettings/Sources/Persistence/KeymanPaths.swift index 3cc1767b49..25dd0b7a82 100644 --- a/mac/KeymanSettings/Sources/Persistence/KeymanPaths.swift +++ b/mac/KeymanSettings/Sources/Persistence/KeymanPaths.swift @@ -9,6 +9,7 @@ */ import Foundation +import OSLog /** * Three directory trees are represented by the following properties, one in active use @@ -52,7 +53,7 @@ public struct KeymanPaths { do { try fileManager.createDirectory(at: fontsDirectory, withIntermediateDirectories: true, attributes: nil) } catch { - print("error: could not create fonts directory: \(error.localizedDescription)") + Logger.setup.error("error: could not create fonts directory: \(error as NSError, privacy: .public)") } } @@ -107,23 +108,21 @@ public struct KeymanPaths { self.keyman19PackagesDirectory = KeymanPaths.buildKeyman19PackagesUrl(container: containerDir) self.keyman19TempDirectory = KeymanPaths.buildKeyman19TempUrl(container: containerDir) - //self.logPaths() + self.logPaths() } - /* - fileprivate func logPaths() { - ConfigLogger.shared.testLogger.debug("documents: \(self.keyman17DocumentsDirectory!.absoluteString)") - ConfigLogger.shared.testLogger.debug("keyman 17 packages: \(self.keyman17PackagesDirectory!.absoluteString)") - - ConfigLogger.shared.testLogger.debug("support directory: \(self.keyman18SupportDirectory!.absoluteString)") - ConfigLogger.shared.testLogger.debug("support keyman directory: \(self.keyman18DataDirectory!.absoluteString)") - ConfigLogger.shared.testLogger.debug("keyman 18 packages: \(self.keyman18PackagesDirectory!.absoluteString)") - - ConfigLogger.shared.testLogger.debug("container: \(self.keyman19ContainerDirectory!.absoluteString)") - ConfigLogger.shared.testLogger.debug("preferences: \(self.keyman19PreferencesDirectory!.absoluteString)") - ConfigLogger.shared.testLogger.debug("keyman 19 packages: \(self.keyman19PackagesDirectory!.absoluteString)") - } - */ + fileprivate func logPaths() { + Logger.setup.debug("documents: \(self.keyman17DocumentsDirectory!.absoluteString)") + Logger.setup.debug("keyman 17 packages: \(self.keyman17PackagesDirectory!.absoluteString)") + + Logger.setup.debug("support directory: \(self.keyman18SupportDirectory!.absoluteString)") + Logger.setup.debug("support keyman directory: \(self.keyman18DataDirectory!.absoluteString)") + Logger.setup.debug("keyman 18 packages: \(self.keyman18PackagesDirectory!.absoluteString)") + + Logger.setup.debug("container: \(self.keyman19ContainerDirectory.absoluteString)") + Logger.setup.debug("preferences: \(self.keyman19PreferencesDirectory.absoluteString)") + Logger.setup.debug("keyman 19 packages: \(self.keyman19PackagesDirectory.absoluteString)") + } /** * build the URL to specified file in the Input Methods directory @@ -142,8 +141,7 @@ public struct KeymanPaths { inputMethodUrl = inputMethodDirectoryUrl.appendingPathComponent(fileName, isDirectory: false) return inputMethodUrl } catch { - // ConfigLogger.shared.testLogger.debug("\(error)") - print("\(error)") + Logger.setup.error("buildInputMethodPathUrl error: \(error as NSError, privacy: .public)") return nil } } @@ -156,6 +154,7 @@ public struct KeymanPaths { let executableName = inputMethodUrl.deletingPathExtension().lastPathComponent return inputMethodUrl.appendingPathComponent("Contents/MacOS/\(executableName)") } else { + Logger.setup.error("buildInputMethodExecutableUrl error: could not build input method executable directory") return nil } } @@ -175,7 +174,7 @@ public struct KeymanPaths { ) return documentsDirectoryUrl } catch { - print("\(error)") + Logger.setup.error("buildDocumentsUrl error: \(error as NSError, privacy: .public)") return nil } } @@ -187,7 +186,7 @@ public struct KeymanPaths { if let keyman17PackagesDirectory = documents?.appendingPathComponent(preKeyman19PackagesDirectoryName, isDirectory: true) { return keyman17PackagesDirectory } else { - print("could not build keyman17 packages directory") + Logger.setup.error("buildKeyman17PackagesUrl error: could not build keyman17 packages directory") return nil } } @@ -208,7 +207,7 @@ public struct KeymanPaths { return supportDirectoryUrl } catch { - print("\(error)") + Logger.setup.error("buildSupportDirectory error: \(error as NSError, privacy: .public)") return nil } } diff --git a/mac/KeymanSettings/Sources/Persistence/PackageInstallHelper.swift b/mac/KeymanSettings/Sources/Persistence/PackageInstallHelper.swift index eda824f973..b3f497b637 100644 --- a/mac/KeymanSettings/Sources/Persistence/PackageInstallHelper.swift +++ b/mac/KeymanSettings/Sources/Persistence/PackageInstallHelper.swift @@ -11,6 +11,7 @@ import Foundation import CoreText +import OSLog public enum PackageInstallationType { case newPackage(String) @@ -69,8 +70,8 @@ public class PackageInstallHelper: Identifiable { * Indicates that a package has been downloaded and can be prepared for installation */ public func packageDownloadComplete(for kmpFileUrl: URL) throws { - print ("packageDownloadComplete \(kmpFileUrl)") - + Logger.data.log("packageDownloadComplete \(kmpFileUrl.path, privacy: .public)") + try self.prepareToInstall(for: kmpFileUrl) } @@ -79,8 +80,8 @@ public class PackageInstallHelper: Identifiable { * */ public func prepareToInstall(for kmpFileUrl: URL) throws { - print ("prepareToInstall \(kmpFileUrl)") - + Logger.data.log("prepareToInstall \(kmpFileUrl.path, privacy: .public)") + do { // unzip to the temp directory try self.packageRepository.unzipKmpFile(at: kmpFileUrl, to: self.temporaryPackageLocation) @@ -103,7 +104,7 @@ public class PackageInstallHelper: Identifiable { self.packageInstallationType = self.determinePackageInstallationType(newPackage: package) } catch { self.cleanupFailedInstallation() - print ("package installation failed with error '\(error)' for \(kmpFileUrl)") + Logger.data.error("package installation failed for \(kmpFileUrl) with error: \(error as NSError, privacy: .public)") throw error } } @@ -112,11 +113,11 @@ public class PackageInstallHelper: Identifiable { * Install the new package and replace existing package if necessary */ public func installPackage() throws { - print ("installPackage \(self.packageToInstall?.packageName ?? "unknown package")") + Logger.data.info ("installPackage \(self.packageToInstall?.packageName ?? "unknown package", privacy: .public)") // prepareToInstall will always set this guard let installationType = self.packageInstallationType else { - print("error: installationType not set before call to installPackage") + Logger.data.error("error: installationType not set before call to installPackage") throw InstallPackageError.internalError } @@ -145,13 +146,13 @@ public class PackageInstallHelper: Identifiable { let comparisonResult = newVersion.compare(existingVersion, options: .numeric) if comparisonResult == .orderedAscending { - print("package downgrade: new version is older than existing version") + Logger.data.info("package downgrade: new version is older than existing version") installationType = PackageInstallationType.replaceNewerPackage(newPackage.packageName, existingVersion, newVersion) } else if comparisonResult == .orderedDescending { - print("package upgrade: new version is newer than existing version") + Logger.data.info("package upgrade: new version is newer than existing version") installationType = PackageInstallationType.replaceOlderPackage(newPackage.packageName, existingVersion, newVersion) } else { - print("new and existing package versions are identical") + Logger.data.info("new and existing package versions are identical") installationType = PackageInstallationType.replaceSameVersionPackage(newPackage.packageName) } } @@ -168,7 +169,7 @@ public class PackageInstallHelper: Identifiable { let fileManager = FileManager.default guard let installLocation = self.installPackageLocation else { - print("error: installPackageLocation not set when installing fonts") + Logger.data.error("error: installPackageLocation not set when installing fonts") return } @@ -179,7 +180,7 @@ public class PackageInstallHelper: Identifiable { includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles]) } catch { - print("error: unable to get contents of directory at \(installLocation.path) with error: \(String(describing: error))") + Logger.data.error("error: unable to get contents of package fonts directory at \(installLocation.path, privacy: .public) with error: \(error as NSError, privacy: .public)") } for fontUrl in fileUrls { @@ -187,14 +188,14 @@ public class PackageInstallHelper: Identifiable { if ext == "ttf" || ext == "otf" { // if a font fails to install, log error and continue guard self.validateFont(at: fontUrl) else { - print("error: the font \(fontUrl.lastPathComponent) is not valid") + Logger.data.error("error: the font \(fontUrl.lastPathComponent, privacy: .public) is not valid") continue } do { try self.copyFontToFontsDirectory(at: fontUrl) try self.registerFontWithSystem(at: fontUrl) } catch { - print("error: the font \(fontUrl.lastPathComponent) could not be installed with error: \(String(describing: error))") + Logger.data.error("error: the font \(fontUrl.lastPathComponent, privacy: .public) could not be installed with error: \(error as NSError, privacy: .public)") } } } @@ -221,12 +222,12 @@ public class PackageInstallHelper: Identifiable { // remove the font from the fonts directory just in case it is an old one if fileManager.fileExists(atPath: fontDestinationUrl.path) { - print("removed existing font: \(fontDestinationUrl.lastPathComponent)") + Logger.data.info("removed existing font: \(fontDestinationUrl.lastPathComponent)") try? fileManager.removeItem(at: fontDestinationUrl) } try fileManager.copyItem(at: fontUrl, to: fontDestinationUrl) - print("added font: \(fontDestinationUrl.lastPathComponent)") + Logger.data.info("added font: \(fontDestinationUrl.lastPathComponent)") } /** @@ -253,12 +254,12 @@ public class PackageInstallHelper: Identifiable { // code 105 = kCTFontManagerErrorAlreadyRegistered // It is safe to ignore because the font is if errorCode == 105 { - print("font \(fontUrl.lastPathComponent) is already registered.") + Logger.data.info("font \(fontUrl.lastPathComponent) is already registered)") continue } // if it's any other error, capture it to throw later - print("registerFontWithSystem failed for \(fontUrl.lastPathComponent), error: \(String(describing: cfError))") + Logger.data.error("registerFontWithSystem failed for \(fontUrl.lastPathComponent), error: \(cfError as CFError, privacy: .public)") registrationError = InstallPackageError.fontRegistrationError } @@ -301,7 +302,7 @@ public class PackageInstallHelper: Identifiable { do { try self.deleteDownloadedKmpFile() } catch { - print("installNewPackage failed to delete downloaded .kmp file: \(self.temporaryKmpFileLocation.lastPathComponent)") + Logger.data.error("installNewPackage failed to delete downloaded .kmp file: \(self.temporaryKmpFileLocation.lastPathComponent, privacy: .public), error: \(error as NSError, privacy: .public)") } } @@ -318,7 +319,7 @@ public class PackageInstallHelper: Identifiable { do { try self.deleteDownloadedKmpFile() } catch { - print("replaceExistingPackageWithNewPackage failed to delete downloaded .kmp file: \(self.temporaryKmpFileLocation.lastPathComponent)") + Logger.data.error("replaceExistingPackageWithNewPackage failed to delete downloaded .kmp file: \(self.temporaryKmpFileLocation.lastPathComponent, privacy: .public), error: \(error as NSError, privacy: .public)") } } try self.movePackageFromTemporaryToInstalled() @@ -334,13 +335,13 @@ public class PackageInstallHelper: Identifiable { do { try self.deleteDownloadedKmpFile() } catch { - print("cleanupFailedInstallation did not delete downloaded .kmp file: \(self.temporaryKmpFileLocation.lastPathComponent)") + Logger.data.error("cleanupFailedInstallation did not delete downloaded .kmp file: \(self.temporaryKmpFileLocation.lastPathComponent, privacy: .public), error: \(error as NSError, privacy: .public)") } } do { try self.deleteUnzippedPackage() } catch { - print("cleanupFailedInstallation did not delete downloaded package: \(self.temporaryPackageLocation.lastPathComponent)") + Logger.data.error("cleanupFailedInstallation did not delete downloaded package: \(self.temporaryKmpFileLocation.lastPathComponent, privacy: .public), error: \(error as NSError, privacy: .public)") } } diff --git a/mac/KeymanSettings/Sources/Persistence/PackageRepository.swift b/mac/KeymanSettings/Sources/Persistence/PackageRepository.swift index 02e4325135..0764763a54 100644 --- a/mac/KeymanSettings/Sources/Persistence/PackageRepository.swift +++ b/mac/KeymanSettings/Sources/Persistence/PackageRepository.swift @@ -9,6 +9,7 @@ */ import Foundation +import OSLog public enum LoadPackageError: LocalizedError { case invalidUrl @@ -65,7 +66,7 @@ public class PackageRepository: PackageRepo { try package.validate() installedPackages.append(package) } catch { - print("validation failed for \(url) with error: \(error)") + Logger.data.error("validation failed for \(url) with error: \(error as NSError, privacy: .public)") } } @@ -78,7 +79,8 @@ public class PackageRepository: PackageRepo { * */ public func loadSinglePackage(packageUrl: URL) throws -> KeymanPackage { - print("loadSinglePackage from url: \(packageUrl)") + Logger.data.info("loadSinglePackage from url: \(packageUrl, privacy: .public)") + guard let source = try readPackageFromDirectory(packageDirectoryUrl: packageUrl) else { throw LoadPackageError.invalidUrl } let package = KeymanPackage(packageUrl: packageUrl, packageSource: source) @@ -90,12 +92,12 @@ public class PackageRepository: PackageRepo { * delete the package from disk */ public func deletePackage(package: KeymanPackage) { - print("deleting package: \(package.sourceDirectoryUrl)") + Logger.data.info("deleting package: \(package.sourceDirectoryUrl, privacy: .public)") do { try FileManager.default.removeItem(at: package.sourceDirectoryUrl) - print("deleted package: \(package.sourceDirectoryUrl)") + Logger.data.info("deleted package: \(package.sourceDirectoryUrl, privacy: .public)") } catch { - print("could not delete directory: \(error.localizedDescription)") + Logger.data.error("could not delete directory: \(error as NSError, privacy: .public)") } } @@ -110,17 +112,17 @@ public class PackageRepository: PackageRepo { // create the keyman-packages directory if it doesn't already exist if !FileManager.default.fileExists(atPath: packageDirectory.path) { try FileManager.default.createDirectory(at: packageDirectory, withIntermediateDirectories: true, attributes: nil) - print("Created directory: \(packageDirectory.path)") + Logger.data.info("Created directory: \(packageDirectory.path, privacy: .public)") } else { - print("Directory already exists: \(packageDirectory.path)") + Logger.data.info("Directory already exists: \(packageDirectory.path, privacy: .public)") } // create the temp directory if it doesn't already exist if !FileManager.default.fileExists(atPath: packageTempDirectory.path) { try FileManager.default.createDirectory(at: packageTempDirectory, withIntermediateDirectories: true, attributes: nil) - print("Created directory: \(packageTempDirectory.path)") + Logger.data.info("Created directory: \(packageTempDirectory.path, privacy: .public)") } else { - print("Directory already exists: \(packageTempDirectory.path)") + Logger.data.info("Directory already exists: \(packageTempDirectory.path, privacy: .public)") } } @@ -141,9 +143,9 @@ public class PackageRepository: PackageRepo { try fileManager.removeItem(at: fileURL) } - print("successfully cleared temp directory") + Logger.data.info("successfully cleared temp directory") } catch { - print("error clearing temp directory: \(error.localizedDescription)") + Logger.data.error("error clearing temp directory: \(error as NSError, privacy: .public)") } } @@ -174,9 +176,9 @@ public class PackageRepository: PackageRepo { public func unzipKmpFile(at kmpFileUrl: URL, to packageDestinationUrl: URL) throws { do { try FileManager.default.unzipItem(at: kmpFileUrl, to: packageDestinationUrl) - print("Successfully unzipped the file!") + Logger.data.info("successfully unzipped the file") } catch { - print("Extraction failed: \(error.localizedDescription)") + Logger.data.error("extraction failed: \(error as NSError, privacy: .public)") throw LoadPackageError.unzipError } } @@ -226,15 +228,15 @@ public class PackageRepository: PackageRepo { packageMap[itemUrl] = packageSource } } catch let error as LoadPackageError { - print("** package at \(itemUrl) could not be loaded: \(error.localizedDescription)") + Logger.data.error("package at \(itemUrl) could not be loaded: \(error as NSError, privacy: .public)") } } } } catch { - print("Failed to read directory: \(error.localizedDescription)") + Logger.data.error("failed to read directory: \(error as NSError, privacy: .public)") } - print("\(packageMap.count) packages read") + Logger.data.info("readPackageSource: \(packageMap.count, privacy: .public) packages read") return packageMap } @@ -242,7 +244,7 @@ public class PackageRepository: PackageRepo { * check the specified directory for the kmp.json file and read it if it exists */ func readPackageFromDirectory(packageDirectoryUrl: URL) throws -> PackageSource? { - print("readPackageFromDirectory from url: \(packageDirectoryUrl)") + Logger.data.info("readPackageFromDirectory from url: \(packageDirectoryUrl, privacy: .public)") var packageSource: PackageSource? = nil let kmpJsonFileUrl = packageDirectoryUrl.appendingPathComponent(packageFileName) @@ -272,7 +274,7 @@ public class PackageRepository: PackageRepo { throw error } catch { // otherwise convert the error to a LoadPackageError error - print("readPackage error: \(error.localizedDescription)") + Logger.data.error("readPackage error: \(error as NSError, privacy: .public)") throw LoadPackageError.kmpJsonFileUnreadable } return packageSource diff --git a/mac/KeymanSettings/Sources/Util/ConfigLogger.swift b/mac/KeymanSettings/Sources/Util/ConfigLogger.swift deleted file mode 100644 index f9322d6a75..0000000000 --- a/mac/KeymanSettings/Sources/Util/ConfigLogger.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Logger.swift -// KeyFig -// -// Created by Shawn - SIL on 12/9/25. -// - -import OSLog - -class ConfigLogger { - //static let shared = ConfigLogger() - - fileprivate let subsystem = ConfigAppUtil.configBundleId - fileprivate let testCategory = "test" - public let testLogger: Logger - - fileprivate init() { - testLogger = Logger(subsystem: subsystem, category: testCategory) - - testLogger.debug("ConfigLogger instance created.") - } -} diff --git a/mac/KeymanSettings/Tests/KeymanSettingsTests/KeymanSettingsTests.swift b/mac/KeymanSettings/Tests/KeymanSettingsTests/KeymanSettingsTests.swift index 84065244d7..7719f9aa43 100644 --- a/mac/KeymanSettings/Tests/KeymanSettingsTests/KeymanSettingsTests.swift +++ b/mac/KeymanSettings/Tests/KeymanSettingsTests/KeymanSettingsTests.swift @@ -9,12 +9,13 @@ import Testing import Foundation +import OSLog @testable import KeymanSettings @Suite("Settings Container") struct SettingsContainersTests { fileprivate init() async throws { - print("init") + Logger.setup.info("init") } @Test("Check settings creation") @MainActor func testSettingsCreation() async throws { @@ -138,7 +139,7 @@ import Foundation @Suite("Check Keyman paths") struct KeymanPathsTests { fileprivate init() async throws { - print("init") + Logger.setup.info("init") } @Test("Check Keyman 17 documents directory") func testKeyman17DocumentsDirectory() async throws { @@ -191,7 +192,7 @@ import Foundation let moabiteKeyboardKey = "/sil_extinct/moabite.kmx" fileprivate init() async throws { - print("init Settings") + Logger.setup.info("init") do { try self.settingsRepo = DefaultsRepository(suiteName: "test.suite.name") } catch UserDefaultsError.unknownSuite { diff --git a/mac/KeymanSettings/Tests/KeymanSettingsTests/RepoStubs.swift b/mac/KeymanSettings/Tests/KeymanSettingsTests/RepoStubs.swift index 3e90186a43..6a99b6d92b 100644 --- a/mac/KeymanSettings/Tests/KeymanSettingsTests/RepoStubs.swift +++ b/mac/KeymanSettings/Tests/KeymanSettingsTests/RepoStubs.swift @@ -49,12 +49,6 @@ class DefaultsRepoStub: DefaultsRepo { } - func logDefaults() { - print("UserDefaults:") - print("\("KMSelectedKeyboardsKey"): \(self.readSelectedKeyboard())") - print("\("KMEnabledKeyboardsKey"): \(self.readEnabledKeyboards())") - } - func clearDefaults() { selectedKeyboard = "" enabledKeyboards = []