diff --git a/Xcodes/Backend/AppState+Install.swift b/Xcodes/Backend/AppState+Install.swift index 215b445..8f41d67 100644 --- a/Xcodes/Backend/AppState+Install.swift +++ b/Xcodes/Backend/AppState+Install.swift @@ -308,11 +308,10 @@ extension AppState { } func enableDeveloperMode() -> AnyPublisher { - if helperInstallState == .notInstalled { - installHelper() - } - - return Current.helper.devToolsSecurityEnable() + installHelperIfNecessary() + .flatMap { + Current.helper.devToolsSecurityEnable() + } .flatMap { Current.helper.addStaffToDevelopersGroup() } @@ -320,20 +319,18 @@ extension AppState { } func approveLicense(for xcode: InstalledXcode) -> AnyPublisher { - if helperInstallState == .notInstalled { - installHelper() - } - - return Current.helper.acceptXcodeLicense(xcode.path.string) + installHelperIfNecessary() + .flatMap { + Current.helper.acceptXcodeLicense(xcode.path.string) + } .eraseToAnyPublisher() } func installComponents(for xcode: InstalledXcode) -> AnyPublisher { - if helperInstallState == .notInstalled { - installHelper() - } - - return Current.helper.runFirstLaunch(xcode.path.string) + installHelperIfNecessary() + .flatMap { + Current.helper.runFirstLaunch(xcode.path.string) + } .flatMap { Current.shell.getUserCacheDir().map { $0.out } .combineLatest( diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index d676bc3..0f34ca8 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -203,9 +203,29 @@ class AppState: ObservableObject { // MARK: - Helper - func installHelper() { - Current.helper.install() - checkIfHelperIsInstalled() + func installHelperIfNecessary() { + installHelperIfNecessary() + .sink( + receiveCompletion: { [unowned self] completion in + if case let .failure(error) = completion { + self.error = error + } + }, + receiveValue: {} + ) + .store(in: &cancellables) + } + + func installHelperIfNecessary() -> AnyPublisher { + Result { + if helperInstallState == .notInstalled { + try Current.helper.install() + checkIfHelperIsInstalled() + } + } + .publisher + .subscribe(on: DispatchQueue.main) + .eraseToAnyPublisher() } private func checkIfHelperIsInstalled() { @@ -320,16 +340,15 @@ class AppState: ObservableObject { } func select(id: Xcode.ID) { - if helperInstallState == .notInstalled { - installHelper() - } - guard let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version == id }), selectPublisher == nil else { return } - selectPublisher = HelperClient().switchXcodePath(installedXcode.path.string) + selectPublisher = installHelperIfNecessary() + .flatMap { + Current.helper.switchXcodePath(installedXcode.path.string) + } .flatMap { [unowned self] _ in self.updateSelectedXcodePath() } diff --git a/Xcodes/Backend/Environment.swift b/Xcodes/Backend/Environment.swift index 16c2826..dea687b 100644 --- a/Xcodes/Backend/Environment.swift +++ b/Xcodes/Backend/Environment.swift @@ -237,7 +237,7 @@ public struct Defaults { private let helperClient = HelperClient() public struct Helper { - var install: () -> Void = helperClient.install + var install: () throws -> Void = helperClient.install var checkIfLatestHelperIsInstalled: () -> AnyPublisher = helperClient.checkIfLatestHelperIsInstalled var getVersion: () -> AnyPublisher = helperClient.getVersion var switchXcodePath: (_ absolutePath: String) -> AnyPublisher = helperClient.switchXcodePath diff --git a/Xcodes/Backend/HelperClient.swift b/Xcodes/Backend/HelperClient.swift index ae72a3e..e917900 100644 --- a/Xcodes/Backend/HelperClient.swift +++ b/Xcodes/Backend/HelperClient.swift @@ -307,7 +307,7 @@ final class HelperClient { // MARK: - Install // From https://github.com/securing/SimpleXPCApp/ - func install() { + func install() throws { Logger.helperClient.info(#function) var authItem = kSMRightBlessPrivilegedHelper.withCString { name in @@ -329,13 +329,19 @@ final class HelperClient { Logger.helperClient.info("\(#function): Finished installation") } catch { Logger.helperClient.error("\(#function): \(error.localizedDescription)") + + throw error } } private func executeAuthorizationFunction(_ authorizationFunction: () -> (OSStatus) ) throws { let osStatus = authorizationFunction() guard osStatus == errAuthorizationSuccess else { - throw HelperClientError.message(String(describing: SecCopyErrorMessageString(osStatus, nil))) + if let message = SecCopyErrorMessageString(osStatus, nil) { + throw HelperClientError.message(String(message as NSString)) + } else { + throw HelperClientError.message("Unknown error") + } } } diff --git a/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift b/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift index 4dc028d..00c03e9 100644 --- a/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift @@ -55,7 +55,7 @@ struct AdvancedPreferencePane: View { HStack { Text("Helper is not installed") Button("Install helper") { - appState.installHelper() + appState.installHelperIfNecessary() } } }