diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 3f5731e..41c1464 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -366,13 +366,13 @@ class AppState: ObservableObject { } // MARK: - Uninstall - func uninstall(id: Xcode.ID) { + func uninstall(xcode: Xcode) { guard - let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version == id }), + let installedXcode = xcode.installPath, uninstallPublisher == nil else { return } - uninstallPublisher = uninstallXcode(path: installedXcode.path) + uninstallPublisher = uninstallXcode(path: installedXcode) .flatMap { [unowned self] _ in self.updateSelectedXcodePath() } @@ -388,10 +388,10 @@ class AppState: ObservableObject { ) } - func reveal(id: Xcode.ID) { + func reveal(xcode: Xcode) { // TODO: show error if not - guard let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version == id }) else { return } - NSWorkspace.shared.activateFileViewerSelecting([installedXcode.path.url]) + guard let installedXcode = xcode.installPath else { return } + NSWorkspace.shared.activateFileViewerSelecting([installedXcode.url]) } /// Make an Xcode active, a.k.a select it, in the `xcode-select` sense. @@ -404,26 +404,26 @@ class AppState: ObservableObject { /// If they consent to installing the helper then this method will be invoked again with `shouldPrepareUserForHelperInstallation` set to false. /// This will install the helper and make the Xcode active. /// - /// - Parameter id: The identifier of the Xcode to make active. + /// - Parameter xcode: The Xcode to make active. /// - Parameter shouldPrepareUserForHelperInstallation: Whether the user should be presented with an alert preparing them for helper installation before making the Xcode version active. - func select(id: Xcode.ID, shouldPrepareUserForHelperInstallation: Bool = true) { + func select(xcode: Xcode, shouldPrepareUserForHelperInstallation: Bool = true) { guard helperInstallState == .installed || shouldPrepareUserForHelperInstallation == false else { isPreparingUserForActionRequiringHelper = { [unowned self] userConsented in guard userConsented else { return } - self.select(id: id, shouldPrepareUserForHelperInstallation: false) + self.select(xcode: xcode, shouldPrepareUserForHelperInstallation: false) } presentedAlert = .privilegedHelper return } - guard - let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version == id }), - selectPublisher == nil + guard + let installedXcode = xcode.installPath, + uninstallPublisher == nil else { return } - + selectPublisher = installHelperIfNecessary() .flatMap { - Current.helper.switchXcodePath(installedXcode.path.string) + Current.helper.switchXcodePath(installedXcode.string) } .flatMap { [unowned self] _ in self.updateSelectedXcodePath() @@ -440,16 +440,22 @@ class AppState: ObservableObject { ) } - func open(id: Xcode.ID) { - guard let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version == id }) else { return } - NSWorkspace.shared.openApplication(at: installedXcode.path.url, configuration: .init()) + func open(xcode: Xcode) { + switch xcode.installState { + case let .installed(path): + NSWorkspace.shared.openApplication(at: path.url, configuration: .init()) + default: + Logger.appState.error("\(xcode.id) is not installed") + return + } } - func copyPath(id: Xcode.ID) { - guard let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version == id }) else { return } + func copyPath(xcode: Xcode) { + guard let installedXcode = xcode.installPath else { return } + NSPasteboard.general.declareTypes([.URL, .string], owner: nil) - NSPasteboard.general.writeObjects([installedXcode.path.url as NSURL]) - NSPasteboard.general.setString(installedXcode.path.string, forType: .string) + NSPasteboard.general.writeObjects([installedXcode.url as NSURL]) + NSPasteboard.general.setString(installedXcode.string, forType: .string) } func updateAllXcodes(availableXcodes: [AvailableXcode], installedXcodes: [InstalledXcode], selectedXcodePath: String?) { diff --git a/Xcodes/Backend/Xcode.swift b/Xcodes/Backend/Xcode.swift index f78ec76..0e724cc 100644 --- a/Xcodes/Backend/Xcode.swift +++ b/Xcodes/Backend/Xcode.swift @@ -3,6 +3,7 @@ import Foundation import Version import struct XCModel.SDKs import struct XCModel.Compilers +import Path struct Xcode: Identifiable, CustomStringConvertible { let version: Version @@ -57,4 +58,13 @@ struct Xcode: Identifiable, CustomStringConvertible { return nil } } + + var installPath: Path? { + switch installState { + case .installed(let path): + return path + default: + return nil + } + } } diff --git a/Xcodes/Backend/XcodeCommands.swift b/Xcodes/Backend/XcodeCommands.swift index 58ee191..2e904ac 100644 --- a/Xcodes/Backend/XcodeCommands.swift +++ b/Xcodes/Backend/XcodeCommands.swift @@ -83,7 +83,7 @@ struct SelectButton: View { private func select() { guard let xcode = xcode else { return } - appState.select(id: xcode.id) + appState.select(xcode: xcode) } } @@ -100,7 +100,7 @@ struct OpenButton: View { private func open() { guard let xcode = xcode else { return } - appState.open(id: xcode.id) + appState.open(xcode: xcode) } } @@ -132,7 +132,7 @@ struct RevealButton: View { private func reveal() { guard let xcode = xcode else { return } - appState.reveal(id: xcode.id) + appState.reveal(xcode: xcode) } } @@ -149,7 +149,7 @@ struct CopyPathButton: View { private func copyPath() { guard let xcode = xcode else { return } - appState.copyPath(id: xcode.id) + appState.copyPath(xcode: xcode) } } diff --git a/Xcodes/Frontend/InfoPane/InfoPane.swift b/Xcodes/Frontend/InfoPane/InfoPane.swift index c00a85c..3ddfda7 100644 --- a/Xcodes/Frontend/InfoPane/InfoPane.swift +++ b/Xcodes/Frontend/InfoPane/InfoPane.swift @@ -31,7 +31,7 @@ struct InfoPane: View { case let .installed(path): HStack { Text(path.string) - Button(action: { appState.reveal(id: xcode.id) }) { + Button(action: { appState.reveal(xcode: xcode) }) { Image(systemName: "arrow.right.circle.fill") } .buttonStyle(PlainButtonStyle()) diff --git a/Xcodes/Frontend/MainWindow.swift b/Xcodes/Frontend/MainWindow.swift index 1235ea4..3403e34 100644 --- a/Xcodes/Frontend/MainWindow.swift +++ b/Xcodes/Frontend/MainWindow.swift @@ -22,7 +22,7 @@ struct MainWindow: View { .alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in Alert(title: Text("Uninstall Xcode \(xcode.description)?"), message: Text("It will be moved to the Trash, but won't be emptied."), - primaryButton: .destructive(Text("Uninstall"), action: { self.appState.uninstall(id: xcode.id) }), + primaryButton: .destructive(Text("Uninstall"), action: { self.appState.uninstall(xcode: xcode) }), secondaryButton: .cancel(Text("Cancel"))) } diff --git a/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift b/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift index 1b7e477..5d88a70 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift @@ -85,7 +85,7 @@ struct XcodeListViewRow: View { .foregroundColor(.green) .help("This is the active version") } else { - Button(action: { appState.select(id: xcode.id) }) { + Button(action: { appState.select(xcode: xcode) }) { Image(systemName: "checkmark.circle") .foregroundColor(.secondary) } @@ -101,7 +101,7 @@ struct XcodeListViewRow: View { private func installControl(for xcode: Xcode) -> some View { switch xcode.installState { case .installed: - Button("OPEN") { appState.open(id: xcode.id) } + Button("OPEN") { appState.open(xcode: xcode) } .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: selected)) .help("Open this version") case .notInstalled: