Fix a bug when an installed version was appended

We were appending a version without appending a corresponding AvailableXcode, and these two arrays were being zipped later so they wouldn't line up. This change simplifies this method a bit by working on only a single array, and then also moves that appending to the end after the array of Xcodes is created.
This commit is contained in:
Brandon Evans 2021-01-16 11:10:23 -07:00
parent 26da7969a1
commit 62237bf4a8
No known key found for this signature in database
GPG key ID: D58A4B8DB64F8E93
3 changed files with 53 additions and 24 deletions

View file

@ -357,48 +357,40 @@ class AppState: ObservableObject {
}
func updateAllXcodes(availableXcodes: [AvailableXcode], installedXcodes: [InstalledXcode], selectedXcodePath: String?) {
// First, adjust all of the available Xcode versions so that available and installed versions line up and the second part of this function works properly.
var allAvailableXcodeVersions = availableXcodes.map { $0.version }
var adjustedAvailableXcodes = availableXcodes
// First, adjust all of the available Xcodes so that available and installed versions line up and the second part of this function works properly.
for installedXcode in installedXcodes {
// We can trust that build metadata identifiers are unique for each version of Xcode, so if we have it then it's all we need.
// If build metadata matches exactly, replace the available version with the installed version.
// This should handle both Xcode Releases versions which can have different prerelease identifiers and Apple versions which rarely have build metadata identifiers.
if let index = allAvailableXcodeVersions.firstIndex(where: { $0.buildMetadataIdentifiers == installedXcode.version.buildMetadataIdentifiers }) {
allAvailableXcodeVersions[index] = installedXcode.version
}
// If an installed version isn't listed online, add the installed version
// Xcode Releases should have all versions
// Apple didn't used to keep all prerelease versions around but has started to recently
else if !allAvailableXcodeVersions.contains(where: { version in
version.isEquivalent(to: installedXcode.version)
}) {
allAvailableXcodeVersions.append(installedXcode.version)
if let index = adjustedAvailableXcodes.map(\.version).firstIndex(where: { $0.buildMetadataIdentifiers == installedXcode.version.buildMetadataIdentifiers }) {
adjustedAvailableXcodes[index].version = installedXcode.version
}
// 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
// This was originally added for Apple versions
else if let index = allAvailableXcodeVersions.firstIndex(where: { version in
version.isEquivalent(to: installedXcode.version) &&
version.buildMetadataIdentifiers.isEmpty
// Not all prerelease Apple versions available online include build metadata
else if let index = adjustedAvailableXcodes.firstIndex(where: { availableXcode in
availableXcode.version.isEquivalent(to: installedXcode.version) &&
availableXcode.version.buildMetadataIdentifiers.isEmpty
}) {
allAvailableXcodeVersions[index] = installedXcode.version
adjustedAvailableXcodes[index].version = installedXcode.version
}
}
// Map all of the available versions into Xcode values that join available and installed Xcode data for display.
allXcodes = zip(allAvailableXcodeVersions, availableXcodes)
.sorted(by: { $0.0 > $1.0 })
.map { availableXcodeVersion, availableXcode in
var newAllXcodes = adjustedAvailableXcodes
.map { availableXcode -> Xcode in
let installedXcode = installedXcodes.first(where: { installedXcode in
availableXcodeVersion.isEquivalent(to: installedXcode.version)
availableXcode.version.isEquivalent(to: installedXcode.version)
})
// If the existing install state is "installing", keep it
let existingXcodeInstallState = allXcodes.first { $0.version == availableXcodeVersion && $0.installing }?.installState
let existingXcodeInstallState = allXcodes.first { $0.version == availableXcode.version && $0.installing }?.installState
// Otherwise, determine it from whether there's an installed Xcode
let defaultXcodeInstallState: XcodeInstallState = installedXcode != nil ? .installed : .notInstalled
return Xcode(
version: availableXcodeVersion,
version: availableXcode.version,
installState: existingXcodeInstallState ?? defaultXcodeInstallState,
selected: installedXcode != nil && selectedXcodePath?.hasPrefix(installedXcode!.path.string) == true,
path: installedXcode?.path.string,
@ -409,6 +401,25 @@ class AppState: ObservableObject {
compilers: availableXcode.compilers
)
}
// If an installed version isn't listed in the available versions, add the installed version
// Xcode Releases should have all versions
// Apple didn't used to keep all prerelease versions around but has started to recently
for installedXcode in installedXcodes {
if !newAllXcodes.contains(where: { xcode in xcode.version.isEquivalent(to: installedXcode.version) }) {
newAllXcodes.append(
Xcode(
version: installedXcode.version,
installState: .installed,
selected: selectedXcodePath?.hasPrefix(installedXcode.path.string) == true,
path: installedXcode.path.string,
icon: NSWorkspace.shared.icon(forFile: installedXcode.path.string)
)
)
}
}
self.allXcodes = newAllXcodes.sorted { $0.version > $1.version }
}
// MARK: - Private

View file

@ -5,7 +5,7 @@ import struct XCModel.Compilers
/// A version of Xcode that's available for installation
public struct AvailableXcode: Codable {
public let version: Version
public var version: Version
public let url: URL
public let filename: String
public let releaseDate: Date?

View file

@ -88,4 +88,22 @@ class AppStateUpdateTests: XCTestCase {
// XCModel types aren't equatable, so just check for non-nil for now
XCTAssertNotNil(subject.allXcodes[0].sdks)
}
func testAppendingInstalledVersionThatIsNotAvailable() {
subject.allXcodes = [
]
subject.updateAllXcodes(
availableXcodes: [
AvailableXcode(version: Version("1.2.3")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil, sdks: .init(iOS: .init("14.3")))
],
installedXcodes: [
// There's a version installed which for some reason isn't listed online
InstalledXcode(path: Path("/Applications/Xcode-0.0.0.app")!)!
],
selectedXcodePath: nil
)
XCTAssertEqual(subject.allXcodes.map(\.version), [Version("1.2.3")!, Version("0.0.0+ABC123")!])
}
}