Merge pull request #730 from XcodesOrg/matt/multipleArch

Support Xcode 26 multiple Architectures
This commit is contained in:
Matt Kiazyk 2025-07-09 21:46:04 -05:00 committed by GitHub
commit a434d26921
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 244 additions and 32 deletions

View file

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

View file

@ -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 = "<group>"; };
CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListViewRow.swift; sourceTree = "<group>"; };
CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = "<group>"; };
D93F95C02E0C8C1A00238FB5 /* TagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagView.swift; sourceTree = "<group>"; };
E81D7E9F2805250100A205FC /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = "<group>"; };
E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeInstallationStepDetailView.swift; sourceTree = "<group>"; };
E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitViewWrapper.swift; sourceTree = "<group>"; };
@ -340,6 +343,7 @@
E8D655BF288DD04700A139C2 /* SelectedActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedActionType.swift; sourceTree = "<group>"; };
E8DA461025FAF7FB002E85EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepDetailView.swift; sourceTree = "<group>"; };
E8EE58BF2E1CC2A50003FA9F /* RuntimeArchitecture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeArchitecture.swift; sourceTree = "<group>"; };
/* 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 */,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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