feat(mac): replaced print statements with OSLog calls

This commit is contained in:
Shawn Schantz 2026-09-01 10:35:11 -04:00
parent 18b19ee280
commit 5250a84c04
21 changed files with 209 additions and 273 deletions

View file

@ -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()

View file

@ -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 {

View file

@ -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

View file

@ -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()

View file

@ -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")
}
}
}

View file

@ -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()

View file

@ -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<InstallationTask>) -> 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")
}
}

View file

@ -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()
}
}

View file

@ -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 {

View file

@ -18,6 +18,5 @@ public protocol DefaultsRepo {
func writeEnabledKeyboards(enabledKeyboardsArray: [String])
func readSelectedKeyboard() -> String
func writeSelectedKeyboard(keyboardName: String)
func logDefaults()
func clearDefaults()
}

View file

@ -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<AnyObject> and then to a Swift String
if let inputMethodEnabled = Unmanaged<AnyObject>.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<AnyObject> and then to a Swift String
if let capable = Unmanaged<AnyObject>.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
}
}

View file

@ -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")
}
}
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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)")
}
}

View file

@ -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

View file

@ -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.")
}
}

View file

@ -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 {

View file

@ -49,12 +49,6 @@ class DefaultsRepoStub: DefaultsRepo {
}
func logDefaults() {
print("UserDefaults:")
print("\("KMSelectedKeyboardsKey"): \(self.readSelectedKeyboard())")
print("\("KMEnabledKeyboardsKey"): \(self.readEnabledKeyboards())")
}
func clearDefaults() {
selectedKeyboard = ""
enabledKeyboards = []