From acd82fa07aab62dd909d4902fd431f3eb40d79f1 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Sat, 26 Dec 2020 20:40:55 -0700 Subject: [PATCH] Extract AppState.XcodeVersion to Xcode --- Xcodes.xcodeproj/project.pbxproj | 4 ++ Xcodes/Backend/AppState.swift | 39 ++++--------- Xcodes/Backend/Xcode.swift | 22 ++++++++ Xcodes/Frontend/XcodeList/XcodeListView.swift | 55 +++++++++---------- 4 files changed, 65 insertions(+), 55 deletions(-) create mode 100644 Xcodes/Backend/Xcode.swift diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 579d372..a17674d 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ CA39711924495F0E00AFFB77 /* AppStoreButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */; }; CA44901F2463AD34003D8213 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA44901E2463AD34003D8213 /* Tag.swift */; }; CA5D781E257365D6008EDE9D /* PinCodeTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA5D781D257365D6008EDE9D /* PinCodeTextView.swift */; }; + CA61A6E0259835580008926E /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA61A6DF259835580008926E /* Xcode.swift */; }; CA735109257BF96D00EA9CF8 /* AttributedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA735108257BF96D00EA9CF8 /* AttributedText.swift */; }; CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA73510C257BFCEF00EA9CF8 /* NSAttributedString+.swift */; }; CA9FF83F2594FBC000E47BAF /* Licenses.rtf in Resources */ = {isa = PBXBuildFile; fileRef = CA9FF83E2594FBC000E47BAF /* Licenses.rtf */; }; @@ -77,6 +78,7 @@ CA44901E2463AD34003D8213 /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = ""; }; CA538A0C255A4F1A00E64DD7 /* AppleAPI */ = {isa = PBXFileReference; lastKnownFileType = folder; name = AppleAPI; path = Xcodes/AppleAPI; sourceTree = ""; }; CA5D781D257365D6008EDE9D /* PinCodeTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinCodeTextView.swift; sourceTree = ""; }; + CA61A6DF259835580008926E /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = ""; }; CA735108257BF96D00EA9CF8 /* AttributedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedText.swift; sourceTree = ""; }; CA73510C257BFCEF00EA9CF8 /* NSAttributedString+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+.swift"; sourceTree = ""; }; CA8FB5F8256E0F9400469DA5 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; @@ -229,6 +231,7 @@ CABFA9A82592EEE900380FEE /* Version+.swift */, CA9FF876259528CC00E47BAF /* Version+XcodeReleases.swift */, CABFA9A62592EEE900380FEE /* Version+Xcode.swift */, + CA61A6DF259835580008926E /* Xcode.swift */, ); path = Backend; sourceTree = ""; @@ -473,6 +476,7 @@ CABFA9C52592EEEA00380FEE /* FileManager+.swift in Sources */, CABFA9CD2592EEEA00380FEE /* Foundation.swift in Sources */, CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */, + CA61A6E0259835580008926E /* Xcode.swift in Sources */, CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */, CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */, CA9FF8522595080100E47BAF /* AcknowledgementsView.swift in Sources */, diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index e0ec6e2..d90fe89 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -5,6 +5,7 @@ import Path import LegibleError import KeychainAccess import SwiftUI +import Version class AppState: ObservableObject { private let client = AppleAPI.Client() @@ -13,10 +14,10 @@ class AppState: ObservableObject { @Published var authenticationState: AuthenticationState = .unauthenticated @Published var availableXcodes: [AvailableXcode] = [] { willSet { - updateAllVersions(newValue) + updateAllXcodes(newValue) } } - var allVersions: [XcodeVersion] = [] + var allXcodes: [Xcode] = [] @Published var updatePublisher: AnyCancellable? var isUpdating: Bool { updatePublisher != nil } @Published var error: AlertContent? @@ -171,27 +172,27 @@ class AppState: ObservableObject { // MARK: - - func install(id: String) { + func install(id: Xcode.ID) { // TODO: } - func uninstall(id: String) { + func uninstall(id: Xcode.ID) { // TODO: } - func reveal(id: String) { + func reveal(id: Xcode.ID) { // TODO: show error if not - guard let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version.xcodeDescription == id }) else { return } + guard let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version == id }) else { return } NSWorkspace.shared.activateFileViewerSelecting([installedXcode.path.url]) } - func select(id: String) { + func select(id: Xcode.ID) { // TODO: } // MARK: - Private - private func updateAllVersions(_ xcodes: [AvailableXcode]) { + private func updateAllXcodes(_ xcodes: [AvailableXcode]) { let installedXcodes = Current.files.installedXcodes(Path.root/"Applications") var allXcodeVersions = xcodes.map { $0.version } for installedXcode in installedXcodes { @@ -210,12 +211,12 @@ class AppState: ObservableObject { } } - allVersions = allXcodeVersions + allXcodes = allXcodeVersions .sorted(by: >) .map { xcodeVersion in let installedXcode = installedXcodes.first(where: { xcodeVersion.isEquivalentForDeterminingIfInstalled(toInstalled: $0.version) }) - return XcodeVersion( - title: xcodeVersion.xcodeDescription, + return Xcode( + version: xcodeVersion, installState: installedXcodes.contains(where: { xcodeVersion.isEquivalentForDeterminingIfInstalled(toInstalled: $0.version) }) ? .installed : .notInstalled, selected: false, path: installedXcode?.path.string @@ -225,22 +226,6 @@ class AppState: ObservableObject { // MARK: - Nested Types - - /// A merging of AvailableXcode and InstalledXcode prepared for display - struct XcodeVersion: Identifiable { - let title: String - let installState: InstallState - let selected: Bool - let path: String? - var id: String { title } - var installed: Bool { installState == .installed } - } - - enum InstallState: Equatable { - case notInstalled - case installing(Progress) - case installed - } struct AlertContent: Identifiable { var title: String diff --git a/Xcodes/Backend/Xcode.swift b/Xcodes/Backend/Xcode.swift new file mode 100644 index 0000000..d2b3ec8 --- /dev/null +++ b/Xcodes/Backend/Xcode.swift @@ -0,0 +1,22 @@ +import Foundation +import Version + +struct Xcode: Identifiable, CustomStringConvertible { + let version: Version + let installState: XcodeInstallState + let selected: Bool + let path: String? + + var id: Version { version } + var installed: Bool { installState == .installed } + + var description: String { + version.xcodeDescription + } +} + +enum XcodeInstallState: Equatable { + case notInstalled + case installing(Progress) + case installed +} diff --git a/Xcodes/Frontend/XcodeList/XcodeListView.swift b/Xcodes/Frontend/XcodeList/XcodeListView.swift index c97b3b6..788bcd4 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListView.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListView.swift @@ -4,27 +4,26 @@ import PromiseKit struct XcodeListView: View { @EnvironmentObject var appState: AppState - @State private var selection = Set() - @State private var rowBeingConfirmedForUninstallation: AppState.XcodeVersion? + @State private var selection = Set() + @State private var xcodeBeingConfirmedForUninstallation: Xcode? @State private var searchText: String = "" @AppStorage("lastUpdated") private var lastUpdated: Double? - @AppStorage("xcodeListCategory") private var category: Category = .all - var visibleVersions: [AppState.XcodeVersion] { - var versions: [AppState.XcodeVersion] + var visibleXcodes: [Xcode] { + var xcodes: [Xcode] switch category { case .all: - versions = appState.allVersions + xcodes = appState.allXcodes case .installed: - versions = appState.allVersions.filter { $0.installed } + xcodes = appState.allXcodes.filter { $0.installed } } if !searchText.isEmpty { - versions = versions.filter { $0.title.contains(searchText) } + xcodes = xcodes.filter { $0.description.contains(searchText) } } - return versions + return xcodes } enum Category: String, CaseIterable, Identifiable, CustomStringConvertible { @@ -42,36 +41,36 @@ struct XcodeListView: View { } var body: some View { - List(visibleVersions, selection: $selection) { row in + List(visibleXcodes, selection: $selection) { xcode in VStack(alignment: .leading) { HStack { - Text(row.title) + Text(xcode.description) .font(.body) - if row.selected { + if xcode.selected { Tag(text: "SELECTED") .foregroundColor(.green) } Spacer() - Button(row.installed ? "INSTALLED" : "INSTALL") { + Button(xcode.installed ? "INSTALLED" : "INSTALL") { print("Installing...") } - .buttonStyle(AppStoreButtonStyle(installed: row.installed, - highlighted: self.selection.contains(row.id))) - .disabled(row.installed) + .buttonStyle(AppStoreButtonStyle(installed: xcode.installed, + highlighted: self.selection.contains(xcode.id))) + .disabled(xcode.installed) } - Text(verbatim: row.path ?? "") + Text(verbatim: xcode.path ?? "") .font(.caption) - .foregroundColor(self.selection.contains(row.id) ? Color(NSColor.selectedMenuItemTextColor) : Color(NSColor.secondaryLabelColor)) + .foregroundColor(self.selection.contains(xcode.id) ? Color(NSColor.selectedMenuItemTextColor) : Color(NSColor.secondaryLabelColor)) } .contextMenu { - Button(action: { row.installed ? self.rowBeingConfirmedForUninstallation = row : self.appState.install(id: row.id) }) { - Text(row.installed ? "Uninstall" : "Install") + Button(action: { xcode.installed ? self.xcodeBeingConfirmedForUninstallation = xcode : self.appState.install(id: xcode.id) }) { + Text(xcode.installed ? "Uninstall" : "Install") } - if row.installed { - Button(action: { self.appState.reveal(id: row.id) }) { + if xcode.installed { + Button(action: { self.appState.reveal(id: xcode.id) }) { Text("Reveal in Finder") } - Button(action: { self.appState.select(id: row.id) }) { + Button(action: { self.appState.select(id: xcode.id) }) { Text("Select") } } @@ -154,11 +153,11 @@ struct XcodeListView_Previews: PreviewProvider { XcodeListView() .environmentObject({ () -> AppState in let a = AppState() - a.allVersions = [ - AppState.XcodeVersion(title: "12.3", installState: .installed, selected: true, path: nil), - AppState.XcodeVersion(title: "12.2", installState: .notInstalled, selected: false, path: nil), - AppState.XcodeVersion(title: "12.1", installState: .notInstalled, selected: false, path: nil), - AppState.XcodeVersion(title: "12.0", installState: .installed, selected: false, path: nil), + a.allXcodes = [ + Xcode(version: Version("12.3.0")!, installState: .installed, selected: true, path: nil), + Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, path: nil), + Xcode(version: Version("12.1.0")!, installState: .notInstalled, selected: false, path: nil), + Xcode(version: Version("12.0.0")!, installState: .installed, selected: false, path: nil), ] return a }())