From e99c9175ebd578968c06fcd4dc386bc6fce02bc9 Mon Sep 17 00:00:00 2001 From: Shawn Schantz Date: Fri, 24 Jul 2026 14:46:29 -0400 Subject: [PATCH] feat(mac): stop attempting to read version of non-existent input method --- .../Installation/InstallationCheck.swift | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/mac/Config/Installation/InstallationCheck.swift b/mac/Config/Installation/InstallationCheck.swift index 3b7ffda4ac..e0a92398a8 100644 --- a/mac/Config/Installation/InstallationCheck.swift +++ b/mac/Config/Installation/InstallationCheck.swift @@ -84,53 +84,60 @@ public class InstallationCheck { self.defaultsRepository = defaultsRepo self.inputMethodUtil = inputMethodUtil self.isEvaluatingInstallation = false + self.configurationVersion = ConfigAppUtil.configAppVersion() - if inputMethodUtil.keymanInputMethodExists() { - self.isInputMethodInstalled = true - self.inputMethodVersion = (try? inputMethodUtil.getKeymanInputMethodVersion()) ?? "unknown" - } else { - self.isInputMethodInstalled = false - self.inputMethodVersion = "unknown" + var keymanIsCurrent = false + var keymanVersion: String = "unknown" + + let keymanExists = inputMethodUtil.keymanInputMethodExists() + if keymanExists { + keymanVersion = (try? inputMethodUtil.getKeymanInputMethodVersion()) ?? "unknown" + keymanIsCurrent = InstallationCheck.isVersionCurrent(inputMethodVersion: keymanVersion, configurationVersion: self.configurationVersion) } - self.configurationVersion = ConfigAppUtil.configAppVersion() - self.isInputMethodCurrent = InstallationCheck.isVersionCurrent(inputMethodVersion: self.inputMethodVersion, configurationVersion: self.configurationVersion) - - self.installationState = nil - - if let installState = self.loadState() { - // if the installationState remains from a different version, then delete it - if installState.keymanVersion != self.inputMethodVersion { - print("removing stale installation state \(installState.keymanVersion) because the current version is \(self.inputMethodVersion)") - self.clearInstallationState() - // this is a new installation - self.isEvaluatingInstallation = true - } else if installState.isNew { - // for a new installation, do not create the installationState until the evaluation is complete - self.isEvaluatingInstallation = true + self.isInputMethodInstalled = keymanExists + self.isInputMethodCurrent = keymanIsCurrent + self.inputMethodVersion = keymanVersion + + if (self.isInputMethodInstalled && self.isInputMethodCurrent) { + // if we have a valid input method, look at the installation state + + if let installState = self.loadState() { + // if a stale installationState remains from a different version, then delete it + if installState.keymanVersion != self.inputMethodVersion { + print("removing stale installation state \(installState.keymanVersion) because the current version is \(self.inputMethodVersion)") + self.clearInstallationState() + // this is a new installation + self.isEvaluatingInstallation = true + } else if installState.isNew { + // for a new installation, do not create the installationState until the evaluation is complete + self.isEvaluatingInstallation = true + } else { + // If we're already in progress or completed or doing a repair, pick up where we left off + // Note that a completed installation will be checked for repairs + self.installationState = installState + self.isEvaluatingInstallation = false + } } else { - // If we're already in progress or completed or doing a repair, pick up where we left off - // Note that a completed installation will need to be checked for repairs - self.installationState = installState - self.isEvaluatingInstallation = false + // if the installationState does not exist, then this is a new installation + // do not create the installationState until the evaluation is complete + self.isEvaluatingInstallation = true } - } else { - // if the installationState does not exist, then this is a new installation - // do not create the installationState until the evaluation is complete - self.isEvaluatingInstallation = true } self.registerObservers() } /** - * Should be called immediately after init to evaluate what is needed for installation. + * Should be called immediately after init to evaluate what is needed for installation + * or, if the installation is complete, whether it needs repairs. * When the notification from the input method is received and the evaluation is done, * the installation can move out of the `evaluatingInstallation` phase */ public func startInstallationEvaluation() { // call the input method to check whether Accessibility permission has been granted - if self.isEvaluatingInstallation || self.installationState?.isComplete == true { + if (self.isInputMethodInstalled && self.isInputMethodCurrent) && + (self.isEvaluatingInstallation || self.installationState?.isComplete == true) { self.inputMethodUtil.doAsyncAccessibilityCheck() } } @@ -400,7 +407,7 @@ public class InstallationCheck { */ func createRepairInstallationState(accessibilityPermissionGranted: Bool) -> InstallationState? { var repairInstallationState: InstallationState? = nil - var repairTasks = self.determineInstallationTasksNeeded(for: accessibilityPermissionGranted) + let repairTasks = self.determineInstallationTasksNeeded(for: accessibilityPermissionGranted) if !repairTasks.isEmpty { repairInstallationState = InstallationState(version: self.inputMethodVersion, isRepair: true, tasks: repairTasks)