mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +00:00
Uninstall a xcode version
This commit is contained in:
parent
38756100b7
commit
7bfb94d75a
5 changed files with 117 additions and 23 deletions
|
|
@ -12,6 +12,7 @@ class AppState: ObservableObject {
|
|||
private let helperClient = HelperClient()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var selectPublisher: AnyCancellable?
|
||||
private var uninstallPublisher: AnyCancellable?
|
||||
|
||||
@Published var authenticationState: AuthenticationState = .unauthenticated
|
||||
@Published var availableXcodes: [AvailableXcode] = [] {
|
||||
|
|
@ -32,7 +33,6 @@ class AppState: ObservableObject {
|
|||
@Published var presentingSignInAlert = false
|
||||
@Published var isProcessingAuthRequest = false
|
||||
@Published var secondFactorData: SecondFactorData?
|
||||
@Published var xcodeBeingConfirmedForUninstallation: Xcode?
|
||||
@Published var helperInstallState: HelperInstallState = .notInstalled
|
||||
|
||||
init() {
|
||||
|
|
@ -200,14 +200,36 @@ class AppState: ObservableObject {
|
|||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
// MARK: - Install
|
||||
|
||||
func install(id: Xcode.ID) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
// MARK: - Uninstall
|
||||
func uninstall(id: Xcode.ID) {
|
||||
// TODO:
|
||||
if helperInstallState == .notInstalled {
|
||||
installHelper()
|
||||
}
|
||||
|
||||
guard
|
||||
let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version == id }),
|
||||
uninstallPublisher == nil
|
||||
else { return }
|
||||
|
||||
uninstallPublisher = HelperClient().uninstallXcode(installedXcode.path)
|
||||
.flatMap { [unowned self] _ in
|
||||
self.updateSelectedXcodePath()
|
||||
}
|
||||
.sink(
|
||||
receiveCompletion: { [unowned self] completion in
|
||||
if case let .failure(error) = completion {
|
||||
self.error = AlertContent(title: "Error uninstalling Xcode", message: error.legibleLocalizedDescription)
|
||||
}
|
||||
self.uninstallPublisher = nil
|
||||
},
|
||||
receiveValue: { _ in }
|
||||
)
|
||||
}
|
||||
|
||||
func reveal(id: Xcode.ID) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import Combine
|
||||
import Foundation
|
||||
import Path
|
||||
|
||||
final class HelperClient {
|
||||
private var connection: NSXPCConnection?
|
||||
|
|
@ -103,4 +104,27 @@ final class HelperClient {
|
|||
.map { $0.0 }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func uninstallXcode(_ path: Path) -> AnyPublisher<Void, Error> {
|
||||
let connectionErrorSubject = PassthroughSubject<String, Error>()
|
||||
|
||||
return Deferred {
|
||||
Future { promise in
|
||||
do {
|
||||
try Current.files.trashItem(at: path.url)
|
||||
promise(.success(()))
|
||||
} catch {
|
||||
promise(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Take values, but fail when connectionErrorSubject fails
|
||||
.zip(
|
||||
connectionErrorSubject
|
||||
.prepend("")
|
||||
.map { _ in Void() }
|
||||
)
|
||||
.map { $0.0 }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ struct XcodeCommands: Commands {
|
|||
var body: some Commands {
|
||||
CommandMenu("Xcode") {
|
||||
Group {
|
||||
|
||||
InstallCommand()
|
||||
|
||||
Divider()
|
||||
|
|
@ -17,6 +18,7 @@ struct XcodeCommands: Commands {
|
|||
OpenCommand()
|
||||
RevealCommand()
|
||||
CopyPathCommand()
|
||||
UninstallCommand()
|
||||
}
|
||||
.environmentObject(appState)
|
||||
}
|
||||
|
|
@ -31,24 +33,15 @@ struct InstallButton: View {
|
|||
let xcode: Xcode?
|
||||
|
||||
var body: some View {
|
||||
Button(action: uninstallOrInstall) {
|
||||
if let xcode = xcode {
|
||||
Text(xcode.installed == true ? "Uninstall" : "Install")
|
||||
.help(xcode.installed == true ? "Uninstall" : "Install")
|
||||
} else {
|
||||
Text("Install")
|
||||
.help("Install")
|
||||
}
|
||||
Button(action: install) {
|
||||
Text("Install")
|
||||
.help("Install")
|
||||
}
|
||||
}
|
||||
|
||||
private func uninstallOrInstall() {
|
||||
private func install() {
|
||||
guard let xcode = xcode else { return }
|
||||
if xcode.installed {
|
||||
appState.xcodeBeingConfirmedForUninstallation = xcode
|
||||
} else {
|
||||
appState.install(id: xcode.id)
|
||||
}
|
||||
appState.install(id: xcode.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +84,30 @@ struct OpenButton: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct UninstallButton: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
let xcode: Xcode?
|
||||
|
||||
@State private var showingAlert = false
|
||||
var alert: Alert {
|
||||
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) }),
|
||||
secondaryButton: .cancel(Text("Cancel")))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
self.showingAlert = true
|
||||
}) {
|
||||
Text("Uninstall")
|
||||
}
|
||||
.foregroundColor(.red)
|
||||
.help("Uninstall")
|
||||
.alert(isPresented:$showingAlert, content: { self.alert })
|
||||
}
|
||||
}
|
||||
|
||||
struct RevealButton: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
let xcode: Xcode?
|
||||
|
|
@ -133,8 +150,8 @@ struct InstallCommand: View {
|
|||
|
||||
var body: some View {
|
||||
InstallButton(xcode: selectedXcode.unwrapped)
|
||||
.keyboardShortcut(selectedXcode.unwrapped?.installed == true ? "u" : "i", modifiers: [.command, .option])
|
||||
.disabled(selectedXcode.unwrapped == nil)
|
||||
.keyboardShortcut("i", modifiers: [.command, .option])
|
||||
.disabled(selectedXcode.unwrapped?.installed == true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,3 +198,14 @@ struct CopyPathCommand: View {
|
|||
.disabled(selectedXcode.unwrapped?.installed != true)
|
||||
}
|
||||
}
|
||||
|
||||
struct UninstallCommand: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode?
|
||||
|
||||
var body: some View {
|
||||
UninstallButton(xcode: selectedXcode.unwrapped)
|
||||
.keyboardShortcut("u", modifiers: [.command, .option])
|
||||
.disabled(selectedXcode.unwrapped?.installed != true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@ struct InfoPane: View {
|
|||
sdks(for: xcode)
|
||||
compilers(for: xcode)
|
||||
|
||||
if xcode.path != nil {
|
||||
VStack(alignment: .leading) {
|
||||
UninstallButton(xcode: xcode)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
} else {
|
||||
|
|
@ -250,6 +255,21 @@ struct InfoPane_Previews: PreviewProvider {
|
|||
})
|
||||
.previewDisplayName("Populated, Uninstalled")
|
||||
|
||||
InfoPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0))
|
||||
.environmentObject(configure(AppState()) {
|
||||
$0.allXcodes = [
|
||||
.init(
|
||||
version: Version(major: 12, minor: 3, patch: 0),
|
||||
installState: .installed,
|
||||
selected: false,
|
||||
path: "/Applications/Xcode-12.3.0.app",
|
||||
icon: nil,
|
||||
sdks: nil,
|
||||
compilers: nil)
|
||||
]
|
||||
})
|
||||
.previewDisplayName("Basic, installed")
|
||||
|
||||
InfoPane(selectedXcodeID: nil)
|
||||
.environmentObject(configure(AppState()) {
|
||||
$0.allXcodes = [
|
||||
|
|
|
|||
|
|
@ -26,15 +26,15 @@ struct XcodeListViewRow: View {
|
|||
installControl(for: xcode)
|
||||
}
|
||||
.contextMenu {
|
||||
InstallButton(xcode: xcode)
|
||||
|
||||
Divider()
|
||||
|
||||
if xcode.installed {
|
||||
SelectButton(xcode: xcode)
|
||||
OpenButton(xcode: xcode)
|
||||
RevealButton(xcode: xcode)
|
||||
CopyPathButton(xcode: xcode)
|
||||
Divider()
|
||||
UninstallButton(xcode: xcode)
|
||||
} else {
|
||||
InstallButton(xcode: xcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue