diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e034a7..6047ad0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,10 +8,10 @@ on: jobs: test: - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 - name: Run tests env: - DEVELOPER_DIR: /Applications/Xcode_16.app + DEVELOPER_DIR: /Applications/Xcode_16.4.app run: xcodebuild test -scheme Xcodes diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 6dd2703..7062ad2 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -115,6 +115,7 @@ CAFE4AB425B7D3AF0064FE51 /* AdvancedPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4AB325B7D3AF0064FE51 /* AdvancedPreferencePane.swift */; }; CAFE4ABC25B7D54B0064FE51 /* UpdatesPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */; }; CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */; }; + D93F95C12E0C8C1A00238FB5 /* TagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93F95C02E0C8C1A00238FB5 /* TagView.swift */; }; E689540325BE8C64000EBCEA /* DockProgress in Frameworks */ = {isa = PBXBuildFile; productRef = E689540225BE8C64000EBCEA /* DockProgress */; }; E81D7EA02805250100A205FC /* Collection+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81D7E9F2805250100A205FC /* Collection+.swift */; }; E832EAF82B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */; }; @@ -139,6 +140,7 @@ E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8DA461025FAF7FB002E85EF /* NotificationsView.swift */; }; E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */; }; E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; }; + E8EE58C02E1CC2A50003FA9F /* RuntimeArchitecture.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8EE58BF2E1CC2A50003FA9F /* RuntimeArchitecture.swift */; }; E8F44A1E296B4CD7002D6592 /* Path in Frameworks */ = {isa = PBXBuildFile; productRef = E8F44A1D296B4CD7002D6592 /* Path */; }; E8FA00542B5B109800769CE0 /* com.xcodesorg.xcodesapp.Helper in Copy Helper */ = {isa = PBXBuildFile; fileRef = CA9FF8AE2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */ = {isa = PBXBuildFile; productRef = E8FD5726291EE4AC001E004C /* AsyncNetworkService */; }; @@ -321,6 +323,7 @@ CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatesPreferencePane.swift; sourceTree = ""; }; CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListViewRow.swift; sourceTree = ""; }; CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = ""; }; + D93F95C02E0C8C1A00238FB5 /* TagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagView.swift; sourceTree = ""; }; E81D7E9F2805250100A205FC /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = ""; }; E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeInstallationStepDetailView.swift; sourceTree = ""; }; E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitViewWrapper.swift; sourceTree = ""; }; @@ -340,6 +343,7 @@ E8D655BF288DD04700A139C2 /* SelectedActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedActionType.swift; sourceTree = ""; }; E8DA461025FAF7FB002E85EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = ""; }; E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepDetailView.swift; sourceTree = ""; }; + E8EE58BF2E1CC2A50003FA9F /* RuntimeArchitecture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeArchitecture.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -387,6 +391,7 @@ 63EAA4E9259944340046AB8F /* Common */ = { isa = PBXGroup; children = ( + D93F95C02E0C8C1A00238FB5 /* TagView.swift */, CAC281CC259F97FA00B8AB0B /* ObservingProgressIndicator.swift */, 63EAA4EA259944450046AB8F /* ProgressButton.swift */, CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */, @@ -657,6 +662,7 @@ E8E98A9425D863B100EC89A0 /* InfoPane */ = { isa = PBXGroup; children = ( + E8EE58BF2E1CC2A50003FA9F /* RuntimeArchitecture.swift */, B0403CEF2AD92D7B00137C09 /* ReleaseNotesView.swift */, B0403CF32AD9381D00137C09 /* SDKsView.swift */, B0403CF52AD9849E00137C09 /* CompilersView.swift */, @@ -932,9 +938,11 @@ E84E4F522B323A5F003F3959 /* CornerRadiusModifier.swift in Sources */, B0403CF22AD934B600137C09 /* CompatibilityView.swift in Sources */, B0403CFE2ADA712C00137C09 /* InfoPaneControls.swift in Sources */, + D93F95C12E0C8C1A00238FB5 /* TagView.swift in Sources */, 53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */, 332807412CA5EA820036F691 /* SignInSecurityKeyTouchView.swift in Sources */, CA61A6E0259835580008926E /* Xcode.swift in Sources */, + E8EE58C02E1CC2A50003FA9F /* RuntimeArchitecture.swift in Sources */, CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */, CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */, B0403CF02AD92D7B00137C09 /* ReleaseNotesView.swift in Sources */, diff --git a/Xcodes/Frontend/Common/TagView.swift b/Xcodes/Frontend/Common/TagView.swift new file mode 100644 index 0000000..9d97614 --- /dev/null +++ b/Xcodes/Frontend/Common/TagView.swift @@ -0,0 +1,24 @@ +// +// TagView.swift +// Xcodes +// +// Created by Matt Kiazyk on 2025-06-25.// + + +import SwiftUI + +struct TagView: View { + let text: String + + var body: some View { + Text(text) + .font(.system(size: 10)) + .foregroundColor(.primary) + .padding(.horizontal, 5) + .padding(.vertical, 2) + .background( + Capsule() + .fill(.quaternary) + ) + } +} diff --git a/Xcodes/Frontend/InfoPane/InfoPane.swift b/Xcodes/Frontend/InfoPane/InfoPane.swift index 962bbdd..f8753cf 100644 --- a/Xcodes/Frontend/InfoPane/InfoPane.swift +++ b/Xcodes/Frontend/InfoPane/InfoPane.swift @@ -35,13 +35,7 @@ struct InfoPane: View { } .xcodesBackground() - VStack { - Text("Platforms") - .font(.title3) - .frame(maxWidth: .infinity, alignment: .leading) - PlatformsView(xcode: xcode) - } - .xcodesBackground() + PlatformsView(xcode: xcode) } .frame(minWidth: 380) diff --git a/Xcodes/Frontend/InfoPane/PlatformsView.swift b/Xcodes/Frontend/InfoPane/PlatformsView.swift index 2ec9b29..6e0b9a1 100644 --- a/Xcodes/Frontend/InfoPane/PlatformsView.swift +++ b/Xcodes/Frontend/InfoPane/PlatformsView.swift @@ -11,7 +11,8 @@ import XcodesKit struct PlatformsView: View { @EnvironmentObject var appState: AppState - + @AppStorage("selectedRuntimeArchitecture") private var selectedRuntimeArchitecture: RuntimeArchitecture = .arm64 + let xcode: Xcode var body: some View { @@ -19,17 +20,50 @@ struct PlatformsView: View { let builds = xcode.sdks?.allBuilds() let runtimes = builds?.flatMap { sdkBuild in appState.downloadableRuntimes.filter { - $0.sdkBuildUpdate?.contains(sdkBuild) ?? false + $0.sdkBuildUpdate?.contains(sdkBuild) ?? false && + ($0.architectures?.isEmpty ?? true || + $0.architectures?.contains(selectedRuntimeArchitecture.rawValue) ?? false) } } - - ForEach(runtimes ?? [], id: \.simulatorVersion.buildUpdate) { runtime in - runtimeView(runtime: runtime) - .frame(minWidth: 200) - .padding() - .background(.quinary) - .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) + + let architectures = Set((runtimes ?? []).flatMap { $0.architectures ?? [] }) + + VStack { + HStack { + Text("Platforms") + .font(.title3) + .frame(maxWidth: .infinity, alignment: .leading) + if !architectures.isEmpty { + Spacer() + Button { + switch selectedRuntimeArchitecture { + case .arm64: selectedRuntimeArchitecture = .x86_64 + case .x86_64: selectedRuntimeArchitecture = .arm64 + } + } label: { + switch selectedRuntimeArchitecture { + case .arm64: + Label(selectedRuntimeArchitecture.displayValue, systemImage: "m4.button.horizontal") + .labelStyle(.trailingIcon) + case .x86_64: + Label(selectedRuntimeArchitecture.displayValue, systemImage: "cpu.fill") + .labelStyle(.trailingIcon) + } + } + } + } + + ForEach(runtimes ?? [], id: \.identifier) { runtime in + runtimeView(runtime: runtime) + .frame(minWidth: 200) + .padding() + .background(.quinary) + .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) + } } + .xcodesBackground() + + } @ViewBuilder @@ -39,6 +73,9 @@ struct PlatformsView: View { runtime.icon() Text("\(runtime.visibleIdentifier)") .font(.headline) + ForEach(runtime.architectures ?? [], id: \.self) { architecture in + TagView(text: architecture) + } pathIfAvailable(xcode: xcode, runtime: runtime) if runtime.installState == .notInstalled { diff --git a/Xcodes/Frontend/InfoPane/RuntimeArchitecture.swift b/Xcodes/Frontend/InfoPane/RuntimeArchitecture.swift new file mode 100644 index 0000000..abbdf45 --- /dev/null +++ b/Xcodes/Frontend/InfoPane/RuntimeArchitecture.swift @@ -0,0 +1,17 @@ +// +// RuntimeArchitecture.swift +// Xcodes +// +// Created by Matt Kiazyk on 2025-07-07. +// + +enum RuntimeArchitecture: String, CaseIterable, Identifiable { + case arm64 + case x86_64 + + var id: Self { self } + + var displayValue: String { + return rawValue + } +} diff --git a/Xcodes/Frontend/XcodeList/BottomStatusBar.swift b/Xcodes/Frontend/XcodeList/BottomStatusBar.swift index ac4b214..3f6e2e6 100644 --- a/Xcodes/Frontend/XcodeList/BottomStatusBar.swift +++ b/Xcodes/Frontend/XcodeList/BottomStatusBar.swift @@ -34,7 +34,7 @@ struct BottomStatusModifier: ViewModifier { } } } - Text("\(Bundle.main.shortVersion!) (\(Bundle.main.version!))") + Text(verbatim: "\(Bundle.main.shortVersion!) (\(Bundle.main.version!))") .font(.subheadline) } .frame(maxWidth: .infinity, maxHeight: 30, alignment: .leading) diff --git a/Xcodes/Resources/Licenses.rtf b/Xcodes/Resources/Licenses.rtf index a10eda3..152e9bf 100644 --- a/Xcodes/Resources/Licenses.rtf +++ b/Xcodes/Resources/Licenses.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf2818 +{\rtf1\ansi\ansicpg1252\cocoartf2822 \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 .SFNS-Regular;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} diff --git a/Xcodes/Resources/Localizable.xcstrings b/Xcodes/Resources/Localizable.xcstrings index e80c58b..daac9c0 100644 --- a/Xcodes/Resources/Localizable.xcstrings +++ b/Xcodes/Resources/Localizable.xcstrings @@ -237,16 +237,6 @@ } } }, - "%@ (%@)" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$@ (%2$@)" - } - } - } - }, "%@ %@ %@" : { "localizations" : { "ar" : { @@ -17919,7 +17909,128 @@ } }, "PIN not set" : { - + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + } + } }, "Platforms" : { "localizations" : { @@ -17947,6 +18058,12 @@ "value" : "Πλατφόρμες" } }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -18314,6 +18431,12 @@ "value" : "Προτιμήσεις" } }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferences" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -18804,6 +18927,12 @@ "value" : "Ανανέωση" } }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Refresh" + } + }, "es" : { "stringUnit" : { "state" : "translated", diff --git a/Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/Runtimes.swift b/Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/Runtimes.swift index e068165..357fa23 100644 --- a/Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/Runtimes.swift +++ b/Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/Runtimes.swift @@ -12,6 +12,7 @@ public struct DownloadableRuntime: Codable, Identifiable, Hashable { public let category: Category public let simulatorVersion: SimulatorVersion public let source: String? + public let architectures: [String]? public let dictionaryVersion: Int public let contentType: ContentType public let platform: Platform @@ -49,10 +50,11 @@ public struct DownloadableRuntime: Codable, Identifiable, Hashable { case name case authentication case sdkBuildUpdate + case architectures } var betaNumber: Int? { - enum Regex { static let shared = try! NSRegularExpression(pattern: "b[0-9]+$") } + enum Regex { static let shared = try! NSRegularExpression(pattern: "b[0-9]+") } guard var foundString = Regex.shared.firstString(in: identifier) else { return nil } foundString.removeFirst() return Int(foundString)! @@ -94,6 +96,7 @@ public struct SDKToSimulatorMapping: Codable { public let sdkBuildUpdate: String public let simulatorBuildUpdate: String public let sdkIdentifier: String + public let downloadableIdentifiers: [String]? } extension DownloadableRuntime {