From dcd4001548e6ddbe4b526d803eff0a2c7cebbce0 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Thu, 4 Feb 2021 19:53:33 -0700 Subject: [PATCH] Merge identical builds into the release version instead of filtering --- Xcodes/Backend/AppState.swift | 46 ++++++++++++--------------- Xcodes/Backend/Xcode.swift | 4 +++ XcodesTests/AppStateUpdateTests.swift | 12 ++----- 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 1a83b01..e302307 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -425,7 +425,7 @@ class AppState: ObservableObject { } func updateAllXcodes(availableXcodes: [AvailableXcode], installedXcodes: [InstalledXcode], selectedXcodePath: String?) { - var adjustedAvailableXcodes = filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers(availableXcodes) + 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. if dataSource == .apple { @@ -449,10 +449,28 @@ class AppState: ObservableObject { // Map all of the available versions into Xcode values that join available and installed Xcode data for display. var newAllXcodes = adjustedAvailableXcodes + .filter { availableXcode in + let availableXcodesWithIdenticalBuildIdentifiers = availableXcodes + .filter({ $0.version.buildMetadataIdentifiers == availableXcode.version.buildMetadataIdentifiers }) + + // 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 + } .map { availableXcode -> Xcode in let installedXcode = installedXcodes.first(where: { installedXcode in availableXcode.version.isEquivalent(to: installedXcode.version) }) + + let identicalBuilds: [Version] + let availableXcodesWithIdenticalBuildIdentifiers = availableXcodes + .filter({ $0.version.buildMetadataIdentifiers == availableXcode.version.buildMetadataIdentifiers }) + if availableXcodesWithIdenticalBuildIdentifiers.count > 1, availableXcode.version.prereleaseIdentifiers.isEmpty { + identicalBuilds = availableXcodesWithIdenticalBuildIdentifiers.map(\.version) + } else { + identicalBuilds = [] + } // If the existing install state is "installing", keep it let existingXcodeInstallState = allXcodes.first { $0.version == availableXcode.version && $0.installState.installing }?.installState @@ -461,7 +479,7 @@ class AppState: ObservableObject { return Xcode( version: availableXcode.version, - identicalBuilds: [], + identicalBuilds: identicalBuilds, installState: existingXcodeInstallState ?? defaultXcodeInstallState, selected: installedXcode != nil && selectedXcodePath?.hasPrefix(installedXcode!.path.string) == true, icon: (installedXcode?.path.string).map(NSWorkspace.shared.icon(forFile:)), @@ -492,30 +510,6 @@ class AppState: ObservableObject { self.allXcodes = newAllXcodes.sorted { $0.version > $1.version } } - /// Xcode Releases may have multiple releases with the same build metadata when a build doesn't change between candidate and final releases. - /// For example, 12.3 RC and 12.3 are both build 12C33 - /// We don't care about that difference, so only keep the final release (GM or Release, in XCModel terms). - /// The downside of this is that a user could technically have both releases installed, and so they won't both be shown in the list, but I think most users wouldn't do this. - func filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers(_ availableXcodes: [AvailableXcode]) -> [AvailableXcode] { - var filteredAvailableXcodes: [AvailableXcode] = [] - for availableXcode in availableXcodes { - if availableXcode.version.buildMetadataIdentifiers.isEmpty { - filteredAvailableXcodes.append(availableXcode) - continue - } - - let availableXcodesWithSameBuildMetadataIdentifiers = availableXcodes - .filter({ $0.version.buildMetadataIdentifiers == availableXcode.version.buildMetadataIdentifiers }) - if availableXcodesWithSameBuildMetadataIdentifiers.count > 1, - availableXcode.version.prereleaseIdentifiers.isEmpty || availableXcode.version.prereleaseIdentifiers == ["GM"] { - filteredAvailableXcodes.append(availableXcode) - } else if availableXcodesWithSameBuildMetadataIdentifiers.count == 1 { - filteredAvailableXcodes.append(availableXcode) - } - } - return filteredAvailableXcodes - } - // MARK: - Private private func uninstallXcode(path: Path) -> AnyPublisher { diff --git a/Xcodes/Backend/Xcode.swift b/Xcodes/Backend/Xcode.swift index a5da1bc..ebf1f23 100644 --- a/Xcodes/Backend/Xcode.swift +++ b/Xcodes/Backend/Xcode.swift @@ -6,6 +6,8 @@ import struct XCModel.Compilers struct Xcode: Identifiable, CustomStringConvertible { let version: Version + /// Other Xcode versions that have the same build identifier + let identicalBuilds: [Version] var installState: XcodeInstallState let selected: Bool let icon: NSImage? @@ -17,6 +19,7 @@ struct Xcode: Identifiable, CustomStringConvertible { init( version: Version, + identicalBuilds: [Version] = [], installState: XcodeInstallState, selected: Bool, icon: NSImage?, @@ -27,6 +30,7 @@ struct Xcode: Identifiable, CustomStringConvertible { downloadFileSize: Int64? = nil ) { self.version = version + self.identicalBuilds = identicalBuilds self.installState = installState self.selected = selected self.icon = icon diff --git a/XcodesTests/AppStateUpdateTests.swift b/XcodesTests/AppStateUpdateTests.swift index bcb9b35..0d95edb 100644 --- a/XcodesTests/AppStateUpdateTests.swift +++ b/XcodesTests/AppStateUpdateTests.swift @@ -145,6 +145,7 @@ class AppStateUpdateTests: XCTestCase { ) XCTAssertEqual(subject.allXcodes.map(\.version), [Version("12.4.0+12D4e")!]) + XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[Version("12.4.0+12D4e")!, Version("12.4.0-RC+12D4e")!]]) } func testIdenticalBuilds_KeepsReleaseVersion_WithPrereleaseInstalled() { @@ -203,15 +204,6 @@ class AppStateUpdateTests: XCTestCase { ) XCTAssertEqual(subject.allXcodes.map(\.version), [Version("12.4.0+12D4e")!]) - } - - func testFilterReleasesThatMatchPrereleases() { - let result = subject.filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers( - [ - AvailableXcode(version: Version("12.3.0+12C33")!, url: URL(string: "https://apple.com")!, filename: "Xcode_12.3.xip", releaseDate: nil), - AvailableXcode(version: Version("12.3.0-RC+12C33")!, url: URL(string: "https://apple.com")!, filename: "Xcode_12.3_RC_1.xip", releaseDate: nil), - ] - ) - XCTAssertEqual(result.map(\.version), [Version("12.3.0+12C33")]) + XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[Version("12.4.0+12D4e")!, Version("12.4.0-RC+12D4e")!]]) } }