Extract AppState.XcodeVersion to Xcode

This commit is contained in:
Brandon Evans 2020-12-26 20:40:55 -07:00
parent 23db486ac6
commit acd82fa07a
No known key found for this signature in database
GPG key ID: D58A4B8DB64F8E93
4 changed files with 65 additions and 55 deletions

View file

@ -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 = "<group>"; };
CA538A0C255A4F1A00E64DD7 /* AppleAPI */ = {isa = PBXFileReference; lastKnownFileType = folder; name = AppleAPI; path = Xcodes/AppleAPI; sourceTree = "<group>"; };
CA5D781D257365D6008EDE9D /* PinCodeTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinCodeTextView.swift; sourceTree = "<group>"; };
CA61A6DF259835580008926E /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
CA735108257BF96D00EA9CF8 /* AttributedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedText.swift; sourceTree = "<group>"; };
CA73510C257BFCEF00EA9CF8 /* NSAttributedString+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+.swift"; sourceTree = "<group>"; };
CA8FB5F8256E0F9400469DA5 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@ -229,6 +231,7 @@
CABFA9A82592EEE900380FEE /* Version+.swift */,
CA9FF876259528CC00E47BAF /* Version+XcodeReleases.swift */,
CABFA9A62592EEE900380FEE /* Version+Xcode.swift */,
CA61A6DF259835580008926E /* Xcode.swift */,
);
path = Backend;
sourceTree = "<group>";
@ -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 */,

View file

@ -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

View file

@ -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
}

View file

@ -4,27 +4,26 @@ import PromiseKit
struct XcodeListView: View {
@EnvironmentObject var appState: AppState
@State private var selection = Set<String>()
@State private var rowBeingConfirmedForUninstallation: AppState.XcodeVersion?
@State private var selection = Set<Version>()
@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
}())