mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +00:00
Support showing multiple architectures
This commit is contained in:
parent
a434d26921
commit
4b9d86b22e
31 changed files with 459 additions and 85 deletions
|
|
@ -48,7 +48,6 @@
|
|||
CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF84D2595079F00E47BAF /* ScrollingTextView.swift */; };
|
||||
CA9FF8522595080100E47BAF /* AcknowledgementsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8512595080100E47BAF /* AcknowledgementsView.swift */; };
|
||||
CA9FF8662595130600E47BAF /* View+IsHidden.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8652595130600E47BAF /* View+IsHidden.swift */; };
|
||||
CA9FF86D25951C6E00E47BAF /* XCModel in Frameworks */ = {isa = PBXBuildFile; productRef = CA9FF86C25951C6E00E47BAF /* XCModel */; };
|
||||
CA9FF877259528CC00E47BAF /* Version+XcodeReleases.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF876259528CC00E47BAF /* Version+XcodeReleases.swift */; };
|
||||
CA9FF87B2595293E00E47BAF /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF87A2595293E00E47BAF /* DataSource.swift */; };
|
||||
CA9FF88125955C7000E47BAF /* AvailableXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF88025955C7000E47BAF /* AvailableXcode.swift */; };
|
||||
|
|
@ -363,7 +362,6 @@
|
|||
CABFA9E42592F08E00380FEE /* Version in Frameworks */,
|
||||
CABFA9FD2592F13300380FEE /* LegibleError in Frameworks */,
|
||||
E689540325BE8C64000EBCEA /* DockProgress in Frameworks */,
|
||||
CA9FF86D25951C6E00E47BAF /* XCModel in Frameworks */,
|
||||
CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */,
|
||||
E83FDC442CBB649100679C6B /* Sparkle in Frameworks */,
|
||||
E862D43B2CC8B26F00BAA376 /* SRP in Frameworks */,
|
||||
|
|
@ -726,7 +724,6 @@
|
|||
CABFA9ED2592F0CC00380FEE /* SwiftSoup */,
|
||||
CABFA9F72592F0F900380FEE /* KeychainAccess */,
|
||||
CABFA9FC2592F13300380FEE /* LegibleError */,
|
||||
CA9FF86C25951C6E00E47BAF /* XCModel */,
|
||||
CAA858CC25A3D8BC00ACF8C0 /* ErrorHandling */,
|
||||
E689540225BE8C64000EBCEA /* DockProgress */,
|
||||
E8FD5726291EE4AC001E004C /* AsyncNetworkService */,
|
||||
|
|
@ -816,7 +813,6 @@
|
|||
CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */,
|
||||
CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */,
|
||||
CABFA9FB2592F13300380FEE /* XCRemoteSwiftPackageReference "LegibleError" */,
|
||||
CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */,
|
||||
CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */,
|
||||
CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */,
|
||||
E689540125BE8C64000EBCEA /* XCRemoteSwiftPackageReference "DockProgress" */,
|
||||
|
|
@ -1504,14 +1500,6 @@
|
|||
minimumVersion = 0.1.4;
|
||||
};
|
||||
};
|
||||
CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/xcodereleases/data";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/RobotsAndPencils/ErrorHandling";
|
||||
|
|
@ -1607,11 +1595,6 @@
|
|||
isa = XCSwiftPackageProductDependency;
|
||||
productName = LibFido2Swift;
|
||||
};
|
||||
CA9FF86C25951C6E00E47BAF /* XCModel */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */;
|
||||
productName = XCModel;
|
||||
};
|
||||
CAA1CB2C255A5262003FD669 /* AppleAPI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = AppleAPI;
|
||||
|
|
|
|||
|
|
@ -28,15 +28,6 @@
|
|||
"version": "0.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "XcodeReleases",
|
||||
"repositoryURL": "https://github.com/xcodereleases/data",
|
||||
"state": {
|
||||
"branch": "main",
|
||||
"revision": "a43ad89e536d7a3da525fcc23fb182c37b756ecc",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "DockProgress",
|
||||
"repositoryURL": "https://github.com/sindresorhus/DockProgress",
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@ extension AppState {
|
|||
self.allXcodes[index].installState = .installing(step)
|
||||
|
||||
let xcode = self.allXcodes[index]
|
||||
Current.notificationManager.scheduleNotification(title: xcode.id.appleDescription, body: step.description, category: .normal)
|
||||
Current.notificationManager.scheduleNotification(title: xcode.version.major.description + "." + xcode.version.appleDescription, body: step.description, category: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import Foundation
|
|||
import Path
|
||||
import Version
|
||||
import SwiftSoup
|
||||
import struct XCModel.Xcode
|
||||
import AppleAPI
|
||||
import XcodesKit
|
||||
|
||||
|
|
@ -211,7 +210,7 @@ extension AppState {
|
|||
private func xcodeReleases() -> AnyPublisher<[AvailableXcode], Error> {
|
||||
Current.network.dataTask(with: URLRequest(url: URL(string: "https://xcodereleases.com/data.json")!))
|
||||
.map(\.data)
|
||||
.decode(type: [XCModel.Xcode].self, decoder: JSONDecoder())
|
||||
.decode(type: [XcodeRelease].self, decoder: JSONDecoder())
|
||||
.map { xcReleasesXcodes in
|
||||
let xcodes = xcReleasesXcodes.compactMap { xcReleasesXcode -> AvailableXcode? in
|
||||
guard
|
||||
|
|
@ -233,7 +232,8 @@ extension AppState {
|
|||
requiredMacOSVersion: xcReleasesXcode.requires,
|
||||
releaseNotesURL: xcReleasesXcode.links?.notes?.url,
|
||||
sdks: xcReleasesXcode.sdks,
|
||||
compilers: xcReleasesXcode.compilers
|
||||
compilers: xcReleasesXcode.compilers,
|
||||
architectures: xcReleasesXcode.architectures
|
||||
)
|
||||
}
|
||||
return xcodes
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ enum PreferenceKey: String {
|
|||
case xcodeListCategory
|
||||
case allowedMajorVersions
|
||||
case hideSupportXcodes
|
||||
case xcodeListArchitectures
|
||||
|
||||
func isManaged() -> Bool { UserDefaults.standard.objectIsForced(forKey: self.rawValue) }
|
||||
}
|
||||
|
|
@ -146,7 +147,7 @@ class AppState: ObservableObject {
|
|||
// MARK: - Publisher Cancellables
|
||||
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
private var installationPublishers: [Version: AnyCancellable] = [:]
|
||||
private var installationPublishers: [XcodeID: AnyCancellable] = [:]
|
||||
internal var runtimePublishers: [String: Task<(), any Error>] = [:]
|
||||
private var selectPublisher: AnyCancellable?
|
||||
private var uninstallPublisher: AnyCancellable?
|
||||
|
|
@ -523,8 +524,8 @@ class AppState: ObservableObject {
|
|||
|
||||
// MARK: - Install
|
||||
|
||||
func checkMinVersionAndInstall(id: Xcode.ID) {
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return }
|
||||
func checkMinVersionAndInstall(id: XcodeID) {
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id.version }) else { return }
|
||||
|
||||
// Check to see if users macOS is supported
|
||||
if let requiredMacOSVersion = availableXcode.requiredMacOSVersion {
|
||||
|
|
@ -550,8 +551,8 @@ class AppState: ObservableObject {
|
|||
return !ProcessInfo.processInfo.isOperatingSystemAtLeast(xcodeMinimumMacOSVersion)
|
||||
}
|
||||
|
||||
func install(id: Xcode.ID) {
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return }
|
||||
func install(id: XcodeID) {
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id.version }) else { return }
|
||||
|
||||
installationPublishers[id] = signInIfNeeded()
|
||||
.handleEvents(
|
||||
|
|
@ -626,7 +627,7 @@ class AppState: ObservableObject {
|
|||
/// Skips using the username/password to log in to Apple, and simply gets a Auth Cookie used in downloading
|
||||
/// As of Nov 2022 this was returning a 403 forbidden
|
||||
func installWithoutLogin(id: Xcode.ID) {
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return }
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id.version }) else { return }
|
||||
|
||||
installationPublishers[id] = self.install(.version(availableXcode), downloader: Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2)
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
|
@ -649,7 +650,7 @@ class AppState: ObservableObject {
|
|||
}
|
||||
|
||||
func cancelInstall(id: Xcode.ID) {
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return }
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id.version }) else { return }
|
||||
|
||||
// Cancel the publisher
|
||||
installationPublishers[id] = nil
|
||||
|
|
@ -767,7 +768,7 @@ class AppState: ObservableObject {
|
|||
config.allowsRunningApplicationSubstitution = false
|
||||
NSWorkspace.shared.openApplication(at: path.url, configuration: config)
|
||||
default:
|
||||
Logger.appState.error("\(xcode.id) is not installed")
|
||||
Logger.appState.error("\(xcode.id.version) is not installed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -863,7 +864,7 @@ class AppState: ObservableObject {
|
|||
// If build metadata matches exactly, replace the available version with the installed version.
|
||||
// This should handle Apple versions from /downloads/more which don't have build metadata identifiers.
|
||||
if let index = adjustedAvailableXcodes.map(\.version).firstIndex(where: { $0.buildMetadataIdentifiers == installedXcode.version.buildMetadataIdentifiers }) {
|
||||
adjustedAvailableXcodes[index].version = installedXcode.version
|
||||
adjustedAvailableXcodes[index].xcodeID = installedXcode.xcodeID
|
||||
}
|
||||
// If an installed version is the same as one that's listed online which doesn't have build metadata, replace it with the installed version
|
||||
// Not all prerelease Apple versions available online include build metadata
|
||||
|
|
@ -871,7 +872,7 @@ class AppState: ObservableObject {
|
|||
availableXcode.version.isEquivalent(to: installedXcode.version) &&
|
||||
availableXcode.version.buildMetadataIdentifiers.isEmpty
|
||||
}) {
|
||||
adjustedAvailableXcodes[index].version = installedXcode.version
|
||||
adjustedAvailableXcodes[index].xcodeID = installedXcode.xcodeID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -888,14 +889,15 @@ class AppState: ObservableObject {
|
|||
// Include this version if there's only one with this build identifier
|
||||
return availableXcodesWithIdenticalBuildIdentifiers.count == 1 ||
|
||||
// Or if there's more than one with this build identifier and this is the release version
|
||||
availableXcodesWithIdenticalBuildIdentifiers.count > 1 && availableXcode.version.prereleaseIdentifiers.isEmpty
|
||||
|
||||
availableXcodesWithIdenticalBuildIdentifiers.count > 1 && (availableXcode.version.prereleaseIdentifiers.isEmpty || availableXcode.architectures?.count ?? 0 != 0)
|
||||
}
|
||||
.map { availableXcode -> Xcode in
|
||||
let installedXcode = installedXcodes.first(where: { installedXcode in
|
||||
availableXcode.version.isEquivalent(to: installedXcode.version)
|
||||
})
|
||||
|
||||
let identicalBuilds: [Version]
|
||||
let identicalBuilds: [XcodeID]
|
||||
let prereleaseAvailableXcodesWithIdenticalBuildIdentifiers = availableXcodes
|
||||
.filter {
|
||||
return $0.version.buildMetadataIdentifiers == availableXcode.version.buildMetadataIdentifiers &&
|
||||
|
|
@ -905,7 +907,7 @@ class AppState: ObservableObject {
|
|||
}
|
||||
// If this is the release version, add the identical builds to it
|
||||
if !prereleaseAvailableXcodesWithIdenticalBuildIdentifiers.isEmpty, availableXcode.version.prereleaseIdentifiers.isEmpty {
|
||||
identicalBuilds = [availableXcode.version] + prereleaseAvailableXcodesWithIdenticalBuildIdentifiers.map(\.version)
|
||||
identicalBuilds = [availableXcode.xcodeID] + prereleaseAvailableXcodesWithIdenticalBuildIdentifiers.map(\.xcodeID)
|
||||
} else {
|
||||
identicalBuilds = []
|
||||
}
|
||||
|
|
@ -926,7 +928,8 @@ class AppState: ObservableObject {
|
|||
releaseDate: availableXcode.releaseDate,
|
||||
sdks: availableXcode.sdks,
|
||||
compilers: availableXcode.compilers,
|
||||
downloadFileSize: availableXcode.fileSize
|
||||
downloadFileSize: availableXcode.fileSize,
|
||||
architectures: availableXcode.architectures
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import Foundation
|
||||
import Version
|
||||
import struct XCModel.SDKs
|
||||
import struct XCModel.Compilers
|
||||
import XcodesKit
|
||||
|
||||
/// A version of Xcode that's available for installation
|
||||
public struct AvailableXcode: Codable {
|
||||
public var version: Version
|
||||
public var version: Version {
|
||||
return xcodeID.version
|
||||
}
|
||||
public let url: URL
|
||||
public let filename: String
|
||||
public let releaseDate: Date?
|
||||
|
|
@ -14,9 +15,11 @@ public struct AvailableXcode: Codable {
|
|||
public let sdks: SDKs?
|
||||
public let compilers: Compilers?
|
||||
public let fileSize: Int64?
|
||||
public let architectures: [Architecture]?
|
||||
public var downloadPath: String {
|
||||
return url.path
|
||||
}
|
||||
public var xcodeID: XcodeID
|
||||
|
||||
public init(
|
||||
version: Version,
|
||||
|
|
@ -27,9 +30,9 @@ public struct AvailableXcode: Codable {
|
|||
releaseNotesURL: URL? = nil,
|
||||
sdks: SDKs? = nil,
|
||||
compilers: Compilers? = nil,
|
||||
fileSize: Int64? = nil
|
||||
fileSize: Int64? = nil,
|
||||
architectures: [Architecture]? = nil
|
||||
) {
|
||||
self.version = version
|
||||
self.url = url
|
||||
self.filename = filename
|
||||
self.releaseDate = releaseDate
|
||||
|
|
@ -38,5 +41,7 @@ public struct AvailableXcode: Codable {
|
|||
self.sdks = sdks
|
||||
self.compilers = compilers
|
||||
self.fileSize = fileSize
|
||||
self.architectures = architectures
|
||||
self.xcodeID = XcodeID(version: version, architectures: architectures)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,12 @@ import Path
|
|||
/// A version of Xcode that's already installed
|
||||
public struct InstalledXcode: Equatable {
|
||||
public let path: Path
|
||||
public let xcodeID: XcodeID
|
||||
|
||||
/// Composed of the bundle short version from Info.plist and the product build version from version.plist
|
||||
public let version: Version
|
||||
public var version: Version {
|
||||
return xcodeID.version
|
||||
}
|
||||
|
||||
public init?(path: Path) {
|
||||
self.path = path
|
||||
|
|
@ -32,11 +36,13 @@ public struct InstalledXcode: Equatable {
|
|||
prereleaseIdentifiers = ["beta"]
|
||||
}
|
||||
|
||||
self.version = Version(major: bundleVersion.major,
|
||||
let version = Version(major: bundleVersion.major,
|
||||
minor: bundleVersion.minor,
|
||||
patch: bundleVersion.patch,
|
||||
prereleaseIdentifiers: prereleaseIdentifiers,
|
||||
buildMetadataIdentifiers: [versionPlist.productBuildVersion].compactMap { $0 })
|
||||
|
||||
self.xcodeID = XcodeID(version: version, architectures: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import struct XCModel.SDKs
|
||||
import XcodesKit
|
||||
import SwiftUI
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import Version
|
||||
import struct XCModel.Xcode
|
||||
import XcodesKit
|
||||
|
||||
extension Version {
|
||||
/// Initialize a Version from an XcodeReleases' XCModel.Xcode
|
||||
///
|
||||
/// This is kinda quick-and-dirty, and it would probably be better for us to adopt something closer to XCModel.Xcode under the hood and map the scraped data to it instead.
|
||||
init?(xcReleasesXcode: XCModel.Xcode) {
|
||||
init?(xcReleasesXcode: XcodeRelease) {
|
||||
var versionString = xcReleasesXcode.version.number ?? ""
|
||||
|
||||
// Append trailing ".0" in order to get a fully-specified version string
|
||||
|
|
|
|||
|
|
@ -1,14 +1,30 @@
|
|||
import AppKit
|
||||
import Foundation
|
||||
import Version
|
||||
import struct XCModel.SDKs
|
||||
import struct XCModel.Compilers
|
||||
import Path
|
||||
import XcodesKit
|
||||
|
||||
public struct XcodeID: Codable, Hashable, Identifiable {
|
||||
public let version: Version
|
||||
public let architectures: [Architecture]?
|
||||
|
||||
public var id: String {
|
||||
let architectures = architectures?.map { $0.rawValue}.joined() ?? ""
|
||||
return version.description + architectures
|
||||
}
|
||||
|
||||
public init(version: Version, architectures: [Architecture]? = nil) {
|
||||
self.version = version
|
||||
self.architectures = architectures
|
||||
}
|
||||
}
|
||||
|
||||
struct Xcode: Identifiable, CustomStringConvertible {
|
||||
let version: Version
|
||||
var version: Version {
|
||||
return id.version
|
||||
}
|
||||
/// Other Xcode versions that have the same build identifier
|
||||
let identicalBuilds: [Version]
|
||||
let identicalBuilds: [XcodeID]
|
||||
var installState: XcodeInstallState
|
||||
let selected: Bool
|
||||
let icon: NSImage?
|
||||
|
|
@ -18,10 +34,12 @@ struct Xcode: Identifiable, CustomStringConvertible {
|
|||
let sdks: SDKs?
|
||||
let compilers: Compilers?
|
||||
let downloadFileSize: Int64?
|
||||
let architectures: [Architecture]?
|
||||
let id: XcodeID
|
||||
|
||||
init(
|
||||
version: Version,
|
||||
identicalBuilds: [Version] = [],
|
||||
identicalBuilds: [XcodeID] = [],
|
||||
installState: XcodeInstallState,
|
||||
selected: Bool,
|
||||
icon: NSImage?,
|
||||
|
|
@ -30,9 +48,9 @@ struct Xcode: Identifiable, CustomStringConvertible {
|
|||
releaseDate: Date? = nil,
|
||||
sdks: SDKs? = nil,
|
||||
compilers: Compilers? = nil,
|
||||
downloadFileSize: Int64? = nil
|
||||
downloadFileSize: Int64? = nil,
|
||||
architectures: [Architecture]? = nil
|
||||
) {
|
||||
self.version = version
|
||||
self.identicalBuilds = identicalBuilds
|
||||
self.installState = installState
|
||||
self.selected = selected
|
||||
|
|
@ -43,10 +61,10 @@ struct Xcode: Identifiable, CustomStringConvertible {
|
|||
self.sdks = sdks
|
||||
self.compilers = compilers
|
||||
self.downloadFileSize = downloadFileSize
|
||||
self.architectures = architectures
|
||||
self.id = XcodeID(version: version, architectures: architectures)
|
||||
}
|
||||
|
||||
var id: Version { version }
|
||||
|
||||
var description: String {
|
||||
version.appleDescription
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import struct XCModel.Compilers
|
||||
import XcodesKit
|
||||
|
||||
struct CompilersView: View {
|
||||
let compilers: Compilers?
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import XcodesKit
|
|||
import Path
|
||||
import SwiftUI
|
||||
import Version
|
||||
import struct XCModel.Compilers
|
||||
import struct XCModel.SDKs
|
||||
|
||||
struct InfoPane: View {
|
||||
let xcode: Xcode
|
||||
|
|
@ -42,7 +40,7 @@ struct InfoPane: View {
|
|||
VStack(alignment: .leading) {
|
||||
ReleaseDateView(date: xcode.releaseDate, url: xcode.releaseNotesURL)
|
||||
CompatibilityView(requiredMacOSVersion: xcode.requiredMacOSVersion)
|
||||
IdenticalBuildsView(builds: xcode.identicalBuilds)
|
||||
IdenticalBuildsView(builds: xcode.identicalBuilds.map { $0.version })
|
||||
SDKandCompilers
|
||||
}
|
||||
.frame(width: 200)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import SwiftUI
|
||||
import Version
|
||||
import XCModel
|
||||
import XcodesKit
|
||||
import Path
|
||||
|
||||
struct InstalledStateButtons: View {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import Version
|
|||
|
||||
struct NotInstalledStateButtons: View {
|
||||
let downloadFileSizeString: String?
|
||||
let id: Version
|
||||
let id: XcodeID
|
||||
|
||||
@EnvironmentObject var appState: AppState
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ struct NotInstalledStateButtons: View {
|
|||
#Preview {
|
||||
NotInstalledStateButtons(
|
||||
downloadFileSizeString: "1,19 GB",
|
||||
id: Version(major: 12, minor: 3, patch: 0)
|
||||
id: XcodeID(version: Version(major: 12, minor: 3, patch: 0), architectures: nil)
|
||||
)
|
||||
.padding()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import struct XCModel.SDKs
|
||||
import XcodesKit
|
||||
|
||||
struct SDKsView: View {
|
||||
let content: String
|
||||
|
|
|
|||
|
|
@ -15,11 +15,12 @@ struct MainWindow: View {
|
|||
// FB8979533 SceneStorage doesn't restore value after app is quit by user
|
||||
@AppStorage("isShowingInfoPane") private var isShowingInfoPane = false
|
||||
@AppStorage("xcodeListCategory") private var category: XcodeListCategory = .all
|
||||
@AppStorage("xcodeListArchitecture") private var architecture: XcodeListArchitecture = .universal
|
||||
@AppStorage("isInstalledOnly") private var isInstalledOnly = false
|
||||
|
||||
var body: some View {
|
||||
NavigationSplitViewWrapper {
|
||||
XcodeListView(selectedXcodeID: $selectedXcodeID, searchText: searchText, category: category, isInstalledOnly: isInstalledOnly)
|
||||
XcodeListView(selectedXcodeID: $selectedXcodeID, searchText: searchText, category: category, isInstalledOnly: isInstalledOnly, architecture: architecture)
|
||||
.layoutPriority(1)
|
||||
.alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in
|
||||
Alert(title: Text(String(format: localizeString("Alert.Uninstall.Title"), xcode.description)),
|
||||
|
|
@ -31,7 +32,8 @@ struct MainWindow: View {
|
|||
.mainToolbar(
|
||||
category: $category,
|
||||
isInstalledOnly: $isInstalledOnly,
|
||||
isShowingInfoPane: $isShowingInfoPane
|
||||
isShowingInfoPane: $isShowingInfoPane,
|
||||
architecture: $architecture
|
||||
)
|
||||
} detail: {
|
||||
Group {
|
||||
|
|
@ -191,11 +193,11 @@ struct MainWindow: View {
|
|||
case let .checkMinSupportedVersion(xcode, deviceVersion):
|
||||
return Alert(
|
||||
title: Text("Alert.MinSupported.Title"),
|
||||
message: Text(String(format: localizeString("Alert.MinSupported.Message"), xcode.version.descriptionWithoutBuildMetadata, xcode.requiredMacOSVersion ?? "", deviceVersion)),
|
||||
message: Text(String(format: localizeString("Alert.MinSupported.Message"), xcode.xcodeID.version.descriptionWithoutBuildMetadata, xcode.requiredMacOSVersion ?? "", deviceVersion)),
|
||||
primaryButton: .default(
|
||||
Text("Install"),
|
||||
action: {
|
||||
self.appState.install(id: xcode.version)
|
||||
self.appState.install(id: xcode.xcodeID)
|
||||
}
|
||||
),
|
||||
secondaryButton: .cancel(Text("Cancel"))
|
||||
|
|
@ -223,7 +225,7 @@ struct MainWindow_Previews: PreviewProvider {
|
|||
MainWindow().environmentObject({ () -> AppState in
|
||||
let a = AppState()
|
||||
a.allXcodes = [
|
||||
Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [Version("12.0.0+1234A")!, Version("12.0.0-RC+1234A")!], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [XcodeID(version: Version("12.0.0+1234A")!), XcodeID(version: Version("12.0.0-RC+1234A")!)], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil),
|
||||
Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, icon: nil),
|
||||
Xcode(version: Version("12.1.0")!, installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 })), selected: false, icon: nil),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ struct MainToolbarModifier: ViewModifier {
|
|||
@Binding var category: XcodeListCategory
|
||||
@Binding var isInstalledOnly: Bool
|
||||
@Binding var isShowingInfoPane: Bool
|
||||
@Binding var architectures: XcodeListArchitecture
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
|
|
@ -23,6 +24,24 @@ struct MainToolbarModifier: ViewModifier {
|
|||
.help("RefreshDescription")
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
switch architectures {
|
||||
case .universal: architectures = .appleSilicon
|
||||
case .appleSilicon: architectures = .universal
|
||||
}
|
||||
}) {
|
||||
switch architectures {
|
||||
case .universal:
|
||||
Label("Universal", systemImage: "cpu.fill")
|
||||
case .appleSilicon:
|
||||
Label("Apple Silicon", systemImage: "m4.button.horizontal")
|
||||
.labelStyle(.trailingIcon)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
.help("FilterAvailableDescription")
|
||||
.disabled(architectures.isManaged)
|
||||
|
||||
Button(action: {
|
||||
switch category {
|
||||
case .all: category = .release
|
||||
|
|
@ -65,13 +84,15 @@ extension View {
|
|||
func mainToolbar(
|
||||
category: Binding<XcodeListCategory>,
|
||||
isInstalledOnly: Binding<Bool>,
|
||||
isShowingInfoPane: Binding<Bool>
|
||||
isShowingInfoPane: Binding<Bool>,
|
||||
architecture: Binding<XcodeListArchitecture>
|
||||
) -> some View {
|
||||
modifier(
|
||||
MainToolbarModifier(
|
||||
category: category,
|
||||
isInstalledOnly: isInstalledOnly,
|
||||
isShowingInfoPane: isShowingInfoPane
|
||||
isShowingInfoPane: isShowingInfoPane,
|
||||
architectures: architecture
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import Foundation
|
||||
import XcodesKit
|
||||
|
||||
enum XcodeListCategory: String, CaseIterable, Identifiable, CustomStringConvertible {
|
||||
case all
|
||||
|
|
@ -17,3 +18,19 @@ enum XcodeListCategory: String, CaseIterable, Identifiable, CustomStringConverti
|
|||
|
||||
var isManaged: Bool { PreferenceKey.xcodeListCategory.isManaged() }
|
||||
}
|
||||
|
||||
enum XcodeListArchitecture: String, CaseIterable, Identifiable, CustomStringConvertible {
|
||||
case universal
|
||||
case appleSilicon
|
||||
|
||||
var id: Self { self }
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .universal: return localizeString("Universal")
|
||||
case .appleSilicon: return localizeString("Apple Silicon")
|
||||
}
|
||||
}
|
||||
|
||||
var isManaged: Bool { PreferenceKey.xcodeListCategory.isManaged() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,16 @@ struct XcodeListView: View {
|
|||
@Binding var selectedXcodeID: Xcode.ID?
|
||||
private let searchText: String
|
||||
private let category: XcodeListCategory
|
||||
private let architecture: XcodeListArchitecture
|
||||
private let isInstalledOnly: Bool
|
||||
@AppStorage(PreferenceKey.allowedMajorVersions.rawValue) private var allowedMajorVersions = Int.max
|
||||
|
||||
init(selectedXcodeID: Binding<Xcode.ID?>, searchText: String, category: XcodeListCategory, isInstalledOnly: Bool) {
|
||||
init(selectedXcodeID: Binding<Xcode.ID?>, searchText: String, category: XcodeListCategory, isInstalledOnly: Bool, architecture: XcodeListArchitecture) {
|
||||
self._selectedXcodeID = selectedXcodeID
|
||||
self.searchText = searchText
|
||||
self.category = category
|
||||
self.isInstalledOnly = isInstalledOnly
|
||||
self.architecture = architecture
|
||||
}
|
||||
|
||||
var visibleXcodes: [Xcode] {
|
||||
|
|
@ -28,6 +30,20 @@ struct XcodeListView: View {
|
|||
xcodes = appState.allXcodes.filter { $0.version.isPrerelease }
|
||||
}
|
||||
|
||||
switch architecture {
|
||||
case .appleSilicon:
|
||||
xcodes = xcodes.filter { $0.architectures == [.arm64] }
|
||||
case .universal:
|
||||
xcodes = xcodes.filter {
|
||||
if let architectures = $0.architectures {
|
||||
return architectures.contains(.x86_64) // we're assuming that if architectures contains x86 then it's universal
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let latestMajor = xcodes.sorted(\.version)
|
||||
.filter { $0.version.isNotPrerelease }
|
||||
.last?
|
||||
|
|
@ -95,11 +111,11 @@ struct PlatformsPocket: View {
|
|||
struct XcodeListView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
XcodeListView(selectedXcodeID: .constant(nil), searchText: "", category: .all, isInstalledOnly: false)
|
||||
XcodeListView(selectedXcodeID: .constant(nil), searchText: "", category: .all, isInstalledOnly: false, architecture: .appleSilicon)
|
||||
.environmentObject({ () -> AppState in
|
||||
let a = AppState()
|
||||
a.allXcodes = [
|
||||
Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [Version("12.0.0+1234A")!, Version("12.0.0-RC+1234A")!], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [XcodeID(version: Version("12.0.0+1234A")!), XcodeID(version: Version("12.0.0-RC+1234A")!)], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil),
|
||||
Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, icon: nil),
|
||||
Xcode(version: Version("12.1.0")!, installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 })), selected: false, icon: nil),
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ struct XcodeListViewRow: View {
|
|||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.accessibility(label: Text("IdenticalBuilds"))
|
||||
.accessibility(value: Text(xcode.identicalBuilds.map(\.appleDescription).joined(separator: ", ")))
|
||||
.accessibility(value: Text(xcode.identicalBuilds.map(\.version.appleDescription).joined(separator: ", ")))
|
||||
.help("IdenticalBuilds.help")
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +156,7 @@ struct XcodeListViewRow_Previews: PreviewProvider {
|
|||
)
|
||||
|
||||
XcodeListViewRow(
|
||||
xcode: Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [Version("12.0.0-RC+1234A")!], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
xcode: Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [XcodeID(version: Version("12.0.0-RC+1234A")!)], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
selected: false,
|
||||
appState: AppState()
|
||||
)
|
||||
|
|
|
|||
6
Xcodes/Resources/Assets.xcassets/Icons/Contents.json
Normal file
6
Xcodes/Resources/Assets.xcassets/Icons/Contents.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
|
@ -4446,6 +4446,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"AppleSilicon" : {
|
||||
|
||||
},
|
||||
"AppUpdates" : {
|
||||
"localizations" : {
|
||||
|
|
@ -22102,6 +22105,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Universal" : {
|
||||
|
||||
},
|
||||
"UnxipExperiment" : {
|
||||
"localizations" : {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Architecture.swift
|
||||
// XcodesKit
|
||||
//
|
||||
// Created by Matt Kiazyk on 2025-08-23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// The name of an Architecture.
|
||||
public enum Architecture: String, Codable, Equatable, Hashable {
|
||||
/// The Arm64 architecture (Apple Silicon)
|
||||
case arm64 = "arm64"
|
||||
/// The X86\_64 architecture (64-bit Intel)
|
||||
case x86_64 = "x86_64"
|
||||
/// The i386 architecture (32-bit Intel)
|
||||
case i386 = "i386"
|
||||
/// The PowerPC architecture (Motorola)
|
||||
case powerPC = "ppc"
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Checksums.swift
|
||||
// xcodereleases
|
||||
//
|
||||
// Created by Xcode Releases on 9/17/20.
|
||||
// Copyright © 2020 Xcode Releases. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct Checksums: Codable {
|
||||
|
||||
public let sha1: String?
|
||||
|
||||
public init(sha1: String? = nil) {
|
||||
self.sha1 = sha1
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Compiler.swift
|
||||
// xcodereleases
|
||||
//
|
||||
// Created by Xcode Releases on 4/4/18.
|
||||
// Copyright © 2018 Xcode Releases. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct Compilers: Codable {
|
||||
public let gcc: Array<XcodeVersion>?
|
||||
public let llvm_gcc: Array<XcodeVersion>?
|
||||
public let llvm: Array<XcodeVersion>?
|
||||
public let clang: Array<XcodeVersion>?
|
||||
public let swift: Array<XcodeVersion>?
|
||||
|
||||
public init(gcc: XcodeVersion? = nil, llvm_gcc: XcodeVersion? = nil, llvm: XcodeVersion? = nil, clang: XcodeVersion? = nil, swift: XcodeVersion? = nil) {
|
||||
self.gcc = gcc.map { [$0] }
|
||||
self.llvm_gcc = llvm_gcc.map { [$0] }
|
||||
self.llvm = llvm.map { [$0] }
|
||||
self.clang = clang.map { [$0] }
|
||||
self.swift = swift.map { [$0] }
|
||||
}
|
||||
|
||||
public init(gcc: Array<XcodeVersion>?, llvm_gcc: Array<XcodeVersion>?, llvm: Array<XcodeVersion>?, clang: Array<XcodeVersion>?, swift: Array<XcodeVersion>?) {
|
||||
self.gcc = gcc?.isEmpty == true ? nil : gcc
|
||||
self.llvm_gcc = llvm_gcc?.isEmpty == true ? nil : llvm_gcc
|
||||
self.llvm = llvm?.isEmpty == true ? nil : llvm
|
||||
self.clang = clang?.isEmpty == true ? nil : clang
|
||||
self.swift = swift?.isEmpty == true ? nil : swift
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Link.swift
|
||||
// xcodereleases
|
||||
//
|
||||
// Created by Xcode Releases on 4/5/18.
|
||||
// Copyright © 2018 Xcode Releases. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct Link: Codable {
|
||||
public let url: URL
|
||||
public let sizeMB: Int?
|
||||
/// The platforms supported by this link, if applicable.
|
||||
public var architectures: [Architecture]?
|
||||
|
||||
// public init(_ string: String, _ size: Int? = nil, _ architectures: [Architecture]? = nil) {
|
||||
// self.url = URL(string: string)!
|
||||
// self.sizeMB = size
|
||||
// self.architectures = architectures
|
||||
// }
|
||||
}
|
||||
|
||||
public struct Links: Codable {
|
||||
public let download: Link?
|
||||
public let notes: Link?
|
||||
|
||||
public init(download: Link? = nil, notes: Link? = nil) {
|
||||
self.download = download
|
||||
self.notes = notes
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Release.swift
|
||||
// xcodereleases
|
||||
//
|
||||
// Created by Xcode Releases on 4/4/18.
|
||||
// Copyright © 2018 Xcode Releases. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum Release: Codable {
|
||||
|
||||
public enum CodingKeys: String, CodingKey {
|
||||
case gm, gmSeed, rc, beta, dp, release
|
||||
}
|
||||
|
||||
public var isGM: Bool {
|
||||
guard case .gm = self else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
case gm
|
||||
case gmSeed(Int)
|
||||
case rc(Int)
|
||||
case beta(Int)
|
||||
case dp(Int)
|
||||
case release
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let _ = try container.decodeIfPresent(Bool.self, forKey: .gm) {
|
||||
self = .gm
|
||||
} else if let v = try container.decodeIfPresent(Int.self, forKey: .gmSeed) {
|
||||
self = .gmSeed(v)
|
||||
} else if let v = try container.decodeIfPresent(Int.self, forKey: .rc) {
|
||||
self = .rc(v)
|
||||
} else if let v = try container.decodeIfPresent(Int.self, forKey: .beta) {
|
||||
self = .beta(v)
|
||||
} else if let v = try container.decodeIfPresent(Int.self, forKey: .dp) {
|
||||
self = .dp(v)
|
||||
} else if let _ = try container.decodeIfPresent(Bool.self, forKey: .release) {
|
||||
self = .release
|
||||
} else {
|
||||
fatalError("Unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
switch self {
|
||||
case .gm: try container.encode(true, forKey: .gm)
|
||||
case .gmSeed(let v): try container.encode(v, forKey: .gmSeed)
|
||||
case .rc(let v): try container.encode(v, forKey: .rc)
|
||||
case .beta(let v): try container.encode(v, forKey: .beta)
|
||||
case .dp(let v): try container.encode(v, forKey: .dp)
|
||||
case .release: try container.encode(true, forKey: .release)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// SDKs.swift
|
||||
// xcodereleases
|
||||
//
|
||||
// Created by Xcode Releases on 4/4/18.
|
||||
// Copyright © 2018 Xcode Releases. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct SDKs: Codable {
|
||||
public let macOS: Array<XcodeVersion>?
|
||||
public let iOS: Array<XcodeVersion>?
|
||||
public let watchOS: Array<XcodeVersion>?
|
||||
public let tvOS: Array<XcodeVersion>?
|
||||
public let visionOS: Array<XcodeVersion>?
|
||||
|
||||
public init(macOS: XcodeVersion? = nil, iOS: XcodeVersion? = nil, watchOS: XcodeVersion? = nil, tvOS: XcodeVersion? = nil, visionOS: XcodeVersion? = nil) {
|
||||
self.macOS = macOS.map { [$0] }
|
||||
self.iOS = iOS.map { [$0] }
|
||||
self.watchOS = watchOS.map { [$0] }
|
||||
self.tvOS = tvOS.map { [$0] }
|
||||
self.visionOS = visionOS.map { [$0] }
|
||||
}
|
||||
|
||||
public init(macOS: Array<XcodeVersion>?, iOS: XcodeVersion? = nil, watchOS: XcodeVersion? = nil, tvOS: XcodeVersion? = nil, visionOS: XcodeVersion? = nil) {
|
||||
self.macOS = macOS?.isEmpty == true ? nil : macOS
|
||||
self.iOS = iOS.map { [$0] }
|
||||
self.watchOS = watchOS.map { [$0] }
|
||||
self.tvOS = tvOS.map { [$0] }
|
||||
self.visionOS = visionOS.map { [$0] }
|
||||
}
|
||||
|
||||
public init(macOS: Array<XcodeVersion>?, iOS: Array<XcodeVersion>?, watchOS: XcodeVersion? = nil, tvOS: XcodeVersion? = nil, visionOS: XcodeVersion? = nil) {
|
||||
self.macOS = macOS?.isEmpty == true ? nil : macOS
|
||||
self.iOS = iOS?.isEmpty == true ? nil : iOS
|
||||
self.watchOS = watchOS.map { [$0] }
|
||||
self.tvOS = tvOS.map { [$0] }
|
||||
self.visionOS = visionOS.map { [$0] }
|
||||
}
|
||||
|
||||
public init(macOS: Array<XcodeVersion>?, iOS: Array<XcodeVersion>?, watchOS: Array<XcodeVersion>?, tvOS: XcodeVersion? = nil, visionOS: XcodeVersion? = nil) {
|
||||
self.macOS = macOS?.isEmpty == true ? nil : macOS
|
||||
self.iOS = iOS?.isEmpty == true ? nil : iOS
|
||||
self.watchOS = watchOS?.isEmpty == true ? nil : watchOS
|
||||
self.tvOS = tvOS.map { [$0] }
|
||||
self.visionOS = visionOS.map { [$0] }
|
||||
}
|
||||
|
||||
public init(macOS: Array<XcodeVersion>?, iOS: Array<XcodeVersion>?, watchOS: Array<XcodeVersion>?, tvOS: Array<XcodeVersion>?, visionOS: Array<XcodeVersion>?) {
|
||||
self.macOS = macOS?.isEmpty == true ? nil : macOS
|
||||
self.iOS = iOS?.isEmpty == true ? nil : iOS
|
||||
self.watchOS = watchOS?.isEmpty == true ? nil : watchOS
|
||||
self.tvOS = tvOS?.isEmpty == true ? nil : tvOS
|
||||
self.visionOS = visionOS?.isEmpty == true ? nil : visionOS
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Xcode.swift
|
||||
// xcodereleases
|
||||
//
|
||||
// Created by Xcode Releases on 4/3/18.
|
||||
// Copyright © 2018 Xcode Releases. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct XcodeRelease: Codable {
|
||||
public let name: String
|
||||
public let version: XcodeVersion
|
||||
public let date: YMD
|
||||
public let requires: String
|
||||
public let sdks: SDKs?
|
||||
public let compilers: Compilers?
|
||||
public let links: Links?
|
||||
public let checksums: Checksums?
|
||||
|
||||
public var architectures: [Architecture]? {
|
||||
return links.flatMap { $0.download?.architectures }
|
||||
}
|
||||
|
||||
public init(name: String = "Xcode", version: XcodeVersion, date: (Int, Int, Int), requires: String, sdks: SDKs? = nil, compilers: Compilers? = nil, links: Links? = nil, checksums: Checksums? = nil) {
|
||||
self.name = name
|
||||
self.version = version;
|
||||
self.date = YMD(date);
|
||||
self.requires = requires;
|
||||
self.sdks = sdks;
|
||||
self.compilers = compilers
|
||||
self.links = links
|
||||
self.checksums = checksums
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Version.swift
|
||||
// xcodereleases
|
||||
//
|
||||
// Created by Xcode Releases on 4/4/18.
|
||||
// Copyright © 2018 Xcode Releases. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public typealias V = XcodeVersion
|
||||
public struct XcodeVersion: Codable {
|
||||
public let number: String?
|
||||
public let build: String?
|
||||
public let release: Release
|
||||
|
||||
public init(_ build: String, _ number: String? = nil, _ release: Release = .release) {
|
||||
self.number = number; self.build = build; self.release = release
|
||||
}
|
||||
|
||||
public init(number: String, _ build: String? = nil, _ release: Release = .release) {
|
||||
self.number = number; self.build = build; self.release = release
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// YMD.swift
|
||||
// xcodereleases
|
||||
//
|
||||
// Created by Xcode Releases on 4/4/18.
|
||||
// Copyright © 2018 Xcode Releases. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct YMD: Codable {
|
||||
public let year: Int
|
||||
public let month: Int
|
||||
public let day: Int
|
||||
|
||||
public init(_ ymd: (Int, Int, Int)) {
|
||||
self.year = ymd.0; self.month = ymd.1; self.day = ymd.2
|
||||
}
|
||||
|
||||
public init(_ year: Int, _ month: Int, _ day: Int) {
|
||||
self.year = year; self.month = month; self.day = day
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue