Skip to content

Commit b650261

Browse files
authored
Merge pull request #436 from thai-d-v/fix-app-state
Clean up code in `InfoPane` to be more robust
2 parents 5b18a90 + a596e5f commit b650261

18 files changed

Lines changed: 749 additions & 407 deletions

Xcodes.xcodeproj/project.pbxproj

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,21 @@
99
/* Begin PBXBuildFile section */
1010
36741BFD291E4FDB00A85AAE /* DownloadPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36741BFC291E4FDB00A85AAE /* DownloadPreferencePane.swift */; };
1111
36741BFF291E50F500A85AAE /* FileError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36741BFE291E50F500A85AAE /* FileError.swift */; };
12-
536CFDD2263C94DE00026CE0 /* SignedInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536CFDD1263C94DE00026CE0 /* SignedInView.swift */; };
12+
536CFDD2263C94DE00026CE0 /* SignedInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536CFDD1263C94DE00026CE0 /* SignedInView.swift */; };
1313
536CFDD4263C9A8000026CE0 /* XcodesSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536CFDD3263C9A8000026CE0 /* XcodesSheet.swift */; };
1414
53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CBAB2B263DCC9100410495 /* XcodesAlert.swift */; };
1515
63EAA4EB259944450046AB8F /* ProgressButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63EAA4EA259944450046AB8F /* ProgressButton.swift */; };
16+
B0403CF02AD92D7B00137C09 /* ReleaseNotesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CEF2AD92D7B00137C09 /* ReleaseNotesView.swift */; };
17+
B0403CF22AD934B600137C09 /* CompatibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF12AD934B600137C09 /* CompatibilityView.swift */; };
18+
B0403CF42AD9381D00137C09 /* SDKsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF32AD9381D00137C09 /* SDKsView.swift */; };
19+
B0403CF62AD9849E00137C09 /* CompilersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF52AD9849E00137C09 /* CompilersView.swift */; };
20+
B0403CF82AD991F800137C09 /* UnselectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF72AD991F800137C09 /* UnselectedView.swift */; };
21+
B0403CFA2AD9942A00137C09 /* NotInstalledStateButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CF92AD9942A00137C09 /* NotInstalledStateButtons.swift */; };
22+
B0403CFC2AD9A6BF00137C09 /* InstalledStateButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CFB2AD9A6BF00137C09 /* InstalledStateButtons.swift */; };
23+
B0403CFE2ADA712C00137C09 /* InfoPaneControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0403CFD2ADA712C00137C09 /* InfoPaneControls.swift */; };
24+
B0C6AD042AD6E65700E64698 /* ReleaseDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6AD032AD6E65700E64698 /* ReleaseDateView.swift */; };
25+
B0C6AD0B2AD9178E00E64698 /* IdenticalBuildView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6AD0A2AD9178E00E64698 /* IdenticalBuildView.swift */; };
26+
B0C6AD0D2AD91D7900E64698 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6AD0C2AD91D7900E64698 /* IconView.swift */; };
1627
CA11E7BA2598476C00D2EE1C /* XcodeCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA11E7B92598476C00D2EE1C /* XcodeCommands.swift */; };
1728
CA2518EC25A7FF2B00F08414 /* AppStateUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2518EB25A7FF2B00F08414 /* AppStateUpdateTests.swift */; };
1829
CA25192A25A9644800F08414 /* XcodeInstallState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA25192925A9644800F08414 /* XcodeInstallState.swift */; };
@@ -192,6 +203,17 @@
192203
A0187D39285792D1002F46F9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
193204
AAB037D32839BD4700017680 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; };
194205
AB4EB0DE28541FA000FF3B1D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
206+
B0403CEF2AD92D7B00137C09 /* ReleaseNotesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseNotesView.swift; sourceTree = "<group>"; };
207+
B0403CF12AD934B600137C09 /* CompatibilityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompatibilityView.swift; sourceTree = "<group>"; };
208+
B0403CF32AD9381D00137C09 /* SDKsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKsView.swift; sourceTree = "<group>"; };
209+
B0403CF52AD9849E00137C09 /* CompilersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompilersView.swift; sourceTree = "<group>"; };
210+
B0403CF72AD991F800137C09 /* UnselectedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnselectedView.swift; sourceTree = "<group>"; };
211+
B0403CF92AD9942A00137C09 /* NotInstalledStateButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotInstalledStateButtons.swift; sourceTree = "<group>"; };
212+
B0403CFB2AD9A6BF00137C09 /* InstalledStateButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledStateButtons.swift; sourceTree = "<group>"; };
213+
B0403CFD2ADA712C00137C09 /* InfoPaneControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPaneControls.swift; sourceTree = "<group>"; };
214+
B0C6AD032AD6E65700E64698 /* ReleaseDateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseDateView.swift; sourceTree = "<group>"; };
215+
B0C6AD0A2AD9178E00E64698 /* IdenticalBuildView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdenticalBuildView.swift; sourceTree = "<group>"; };
216+
B0C6AD0C2AD91D7900E64698 /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = "<group>"; };
195217
B648F22B2810C1130096781B /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
196218
C0AE7FA4283002DC00DA63D2 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
197219
CA11E7B92598476C00D2EE1C /* XcodeCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeCommands.swift; sourceTree = "<group>"; };
@@ -615,8 +637,19 @@
615637
E8E98A9425D863B100EC89A0 /* InfoPane */ = {
616638
isa = PBXGroup;
617639
children = (
640+
B0403CEF2AD92D7B00137C09 /* ReleaseNotesView.swift */,
641+
B0403CF32AD9381D00137C09 /* SDKsView.swift */,
642+
B0403CF52AD9849E00137C09 /* CompilersView.swift */,
643+
B0403CF12AD934B600137C09 /* CompatibilityView.swift */,
618644
CAFBDC67259A308B003DCC5A /* InfoPane.swift */,
645+
B0403CFD2ADA712C00137C09 /* InfoPaneControls.swift */,
646+
B0403CF72AD991F800137C09 /* UnselectedView.swift */,
647+
B0403CF92AD9942A00137C09 /* NotInstalledStateButtons.swift */,
648+
B0403CFB2AD9A6BF00137C09 /* InstalledStateButtons.swift */,
619649
E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */,
650+
B0C6AD032AD6E65700E64698 /* ReleaseDateView.swift */,
651+
B0C6AD0A2AD9178E00E64698 /* IdenticalBuildView.swift */,
652+
B0C6AD0C2AD91D7900E64698 /* IconView.swift */,
620653
);
621654
path = InfoPane;
622655
sourceTree = "<group>";
@@ -841,7 +874,9 @@
841874
CABFAA492593162500380FEE /* Bundle+InfoPlistValues.swift in Sources */,
842875
CA9FF8662595130600E47BAF /* View+IsHidden.swift in Sources */,
843876
CAE4248C259A68B800B8B246 /* Optional+IsNotNil.swift in Sources */,
877+
B0C6AD0D2AD91D7900E64698 /* IconView.swift in Sources */,
844878
CA9FF9362595B44700E47BAF /* HelperClient.swift in Sources */,
879+
B0C6AD042AD6E65700E64698 /* ReleaseDateView.swift in Sources */,
845880
CAA8587C25A2B37900ACF8C0 /* IsTesting.swift in Sources */,
846881
CABFA9CA2592EEEA00380FEE /* AppState+Update.swift in Sources */,
847882
CA44901F2463AD34003D8213 /* Tag.swift in Sources */,
@@ -859,13 +894,17 @@
859894
CAA1CB45255A5B60003FD669 /* SignIn2FAView.swift in Sources */,
860895
CABFA9C52592EEEA00380FEE /* FileManager+.swift in Sources */,
861896
CABFA9CD2592EEEA00380FEE /* Foundation.swift in Sources */,
897+
B0403CFC2AD9A6BF00137C09 /* InstalledStateButtons.swift in Sources */,
862898
36741BFF291E50F500A85AAE /* FileError.swift in Sources */,
863899
CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */,
900+
B0403CF22AD934B600137C09 /* CompatibilityView.swift in Sources */,
901+
B0403CFE2ADA712C00137C09 /* InfoPaneControls.swift in Sources */,
864902
53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */,
865903
CA42DD6E25AEA8B200BC0B0C /* Logger.swift in Sources */,
866904
CA61A6E0259835580008926E /* Xcode.swift in Sources */,
867905
CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */,
868906
CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */,
907+
B0403CF02AD92D7B00137C09 /* ReleaseNotesView.swift in Sources */,
869908
CAFE4AB425B7D3AF0064FE51 /* AdvancedPreferencePane.swift in Sources */,
870909
CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */,
871910
CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */,
@@ -883,20 +922,25 @@
883922
CABFAA2D2592FBFC00380FEE /* Configure.swift in Sources */,
884923
CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */,
885924
CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */,
925+
B0403CF42AD9381D00137C09 /* SDKsView.swift in Sources */,
886926
CAC9F92D25BCDA4400B4965F /* HelperInstallState.swift in Sources */,
887927
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */,
888928
CAC281CD259F97FA00B8AB0B /* ObservingProgressIndicator.swift in Sources */,
889929
CABFA9C22592EEEA00380FEE /* Publisher+Resumable.swift in Sources */,
930+
B0C6AD0B2AD9178E00E64698 /* IdenticalBuildView.swift in Sources */,
890931
CAFBDC68259A308B003DCC5A /* InfoPane.swift in Sources */,
932+
B0403CF82AD991F800137C09 /* UnselectedView.swift in Sources */,
891933
E87AB3C52939B65E00D72F43 /* Hardware.swift in Sources */,
892934
CAA1CB4D255A5CFD003FD669 /* SignInPhoneListView.swift in Sources */,
893935
CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */,
894936
CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */,
895937
CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */,
896938
CABFA9C72592EEEA00380FEE /* Entry+.swift in Sources */,
939+
B0403CFA2AD9942A00137C09 /* NotInstalledStateButtons.swift in Sources */,
897940
CAE424B4259A764700B8B246 /* AppState+Install.swift in Sources */,
898941
CAE42487259A68A300B8B246 /* XcodeListCategory.swift in Sources */,
899942
CAA858C425A2BE4E00ACF8C0 /* Downloader.swift in Sources */,
943+
B0403CF62AD9849E00137C09 /* CompilersView.swift in Sources */,
900944
E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */,
901945
CA9FF87B2595293E00E47BAF /* DataSource.swift in Sources */,
902946
CABFA9C92592EEEA00380FEE /* URLRequest+Apple.swift in Sources */,

Xcodes/Backend/AppState.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -541,10 +541,10 @@ class AppState: ObservableObject {
541541
)
542542
}
543543

544-
func reveal(xcode: Xcode) {
544+
func reveal(_ path: Path?) {
545545
// TODO: show error if not
546-
guard let installedXcodePath = xcode.installedPath else { return }
547-
NSWorkspace.shared.activateFileViewerSelecting([installedXcodePath.url])
546+
guard let path = path else { return }
547+
NSWorkspace.shared.activateFileViewerSelecting([path.url])
548548
}
549549

550550
func reveal(path: String) {
@@ -630,8 +630,8 @@ class AppState: ObservableObject {
630630
NSPasteboard.general.setString(installedXcodePath.string, forType: .string)
631631
}
632632

633-
func copyReleaseNote(xcode: Xcode) {
634-
guard let url = xcode.releaseNotesURL else { return }
633+
func copyReleaseNote(from url: URL?) {
634+
guard let url = url else { return }
635635
NSPasteboard.general.declareTypes([.URL, .string], owner: nil)
636636
NSPasteboard.general.writeObjects([url as NSURL])
637637
NSPasteboard.general.setString(url.absoluteString, forType: .string)

Xcodes/Backend/XcodeCommands.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ struct RevealButton: View {
151151

152152
private func reveal() {
153153
guard let xcode = xcode else { return }
154-
appState.reveal(xcode: xcode)
154+
appState.reveal(xcode.installedPath)
155155
}
156156
}
157157

@@ -173,8 +173,9 @@ struct CopyPathButton: View {
173173
}
174174

175175
struct CopyReleaseNoteButton: View {
176+
let url: URL?
177+
176178
@EnvironmentObject var appState: AppState
177-
let xcode: Xcode?
178179

179180
var body: some View {
180181
Button(action: copyReleaseNote) {
@@ -184,8 +185,8 @@ struct CopyReleaseNoteButton: View {
184185
}
185186

186187
private func copyReleaseNote() {
187-
guard let xcode = xcode else { return }
188-
appState.copyReleaseNote(xcode: xcode)
188+
guard let url = url else { return }
189+
appState.copyReleaseNote(from: url)
189190
}
190191
}
191192

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// CompatibilityView.swift
3+
// Xcodes
4+
//
5+
// Created by Duong Thai on 13/10/2023.
6+
// Copyright © 2023 Robots and Pencils. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
11+
struct CompatibilityView: View {
12+
let requiredMacOSVersion: String?
13+
14+
var body: some View {
15+
if let requiredMacOSVersion = requiredMacOSVersion {
16+
VStack(alignment: .leading) {
17+
Text("Compatibility")
18+
.font(.headline)
19+
Text(String(format: localizeString("MacOSRequirement"), requiredMacOSVersion))
20+
.font(.subheadline)
21+
}
22+
} else {
23+
EmptyView()
24+
}
25+
}
26+
}
27+
28+
#Preview {
29+
CompatibilityView(requiredMacOSVersion: "10.15.4")
30+
.padding()
31+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// CompilersView.swift
3+
// Xcodes
4+
//
5+
// Created by Duong Thai on 13/10/2023.
6+
// Copyright © 2023 Robots and Pencils. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
import struct XCModel.Compilers
11+
12+
struct CompilersView: View {
13+
let compilers: Compilers?
14+
15+
var body: some View {
16+
if let compilers = compilers {
17+
VStack(alignment: .leading) {
18+
Text("Compilers").font(.headline)
19+
Text(Self.content(from: compilers)).font(.subheadline)
20+
}
21+
} else {
22+
EmptyView()
23+
}
24+
}
25+
26+
static func content(from compilers: Compilers) -> String {
27+
[ ("Swift", compilers.swift),
28+
("Clang", compilers.clang),
29+
("LLVM", compilers.llvm),
30+
("LLVM GCC", compilers.llvm_gcc),
31+
("GCC", compilers.gcc)
32+
].compactMap { // remove nil compiler
33+
guard $0.1 != nil, // has version array
34+
!$0.1!.isEmpty // has at least 1 version
35+
else { return nil }
36+
37+
let numbers = $0.1!.compactMap { $0.number } // remove nil number
38+
guard !numbers.isEmpty // has at least 1 number
39+
else { return nil }
40+
41+
// description for each type of compilers
42+
return "\($0.0): \(numbers.joined(separator: ", "))"
43+
}.joined(separator: "\n")
44+
}
45+
}
46+
47+
#Preview {
48+
let compilers = Compilers(
49+
gcc: .init(number: "4"),
50+
llvm_gcc: .init(number: "213"),
51+
llvm: .init(number: "2.3"),
52+
clang: .init(number: "7.3"),
53+
swift: .init(number: "5.3.2")
54+
)
55+
56+
return CompilersView(compilers: compilers)
57+
.padding()
58+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// IconView.swift
3+
// Xcodes
4+
//
5+
// Created by Duong Thai on 11/10/2023.
6+
// Copyright © 2023 Robots and Pencils. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
import Path
11+
12+
struct IconView: View {
13+
let installState: XcodeInstallState
14+
15+
var body: some View {
16+
if case let .installed(path) = installState {
17+
Image(nsImage: NSWorkspace.shared.icon(forFile: path.string))
18+
} else {
19+
Image(systemName: "app.fill")
20+
.resizable()
21+
.frame(width: 32, height: 32)
22+
.foregroundColor(.secondary)
23+
}
24+
}
25+
}
26+
27+
#Preview("Installed") {
28+
IconView(installState: XcodeInstallState.installed(Path("/Applications/Xcode.app")!))
29+
.frame(width: 300, height: 100)
30+
.padding()
31+
}
32+
33+
#Preview("Not Installed") {
34+
IconView(installState: XcodeInstallState.notInstalled)
35+
.frame(width: 300, height: 100)
36+
.padding()
37+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// IdenticalBuildView.swift
3+
// Xcodes
4+
//
5+
// Created by Duong Thai on 11/10/2023.
6+
// Copyright © 2023 Robots and Pencils. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
import Version
11+
12+
struct IdenticalBuildsView: View {
13+
let builds: [Version]
14+
private let isEmpty: Bool
15+
private let accessibilityDescription: String
16+
17+
var body: some View {
18+
if isEmpty {
19+
EmptyView()
20+
} else {
21+
VStack(alignment: .leading) {
22+
HStack {
23+
Text("IdenticalBuilds")
24+
Image(systemName: "square.fill.on.square.fill")
25+
.foregroundColor(.secondary)
26+
.accessibility(hidden: true)
27+
.help("IdenticalBuilds.help")
28+
}
29+
.font(.headline)
30+
31+
ForEach(builds, id: \.description) { version in
32+
Text("\(version.appleDescription)")
33+
.font(.subheadline)
34+
}
35+
}
36+
.accessibilityElement()
37+
.accessibility(label: Text("IdenticalBuilds"))
38+
.accessibility(value: Text(accessibilityDescription))
39+
.accessibility(hint: Text("IdenticalBuilds.help"))
40+
}
41+
}
42+
43+
init(builds: [Version]) {
44+
self.builds = builds
45+
self.isEmpty = builds.isEmpty
46+
self.accessibilityDescription = builds
47+
.map(\.appleDescription)
48+
.joined(separator: ", ")
49+
}
50+
}
51+
52+
let builds: [Version] = [.init(xcodeVersion: "15.0")!, .init(xcodeVersion: "15.1")!]
53+
54+
#Preview("Has Some Builds") {
55+
IdenticalBuildsView(builds: builds)
56+
.padding()
57+
}
58+
59+
#Preview("No Build") {
60+
IdenticalBuildsView(builds: [])
61+
.padding()
62+
}

0 commit comments

Comments
 (0)