diff --git a/Xcodes/Backend/AppState+Update.swift b/Xcodes/Backend/AppState+Update.swift index 48d9c21..660d2f0 100644 --- a/Xcodes/Backend/AppState+Update.swift +++ b/Xcodes/Backend/AppState+Update.swift @@ -200,6 +200,31 @@ extension AppState { } return xcodes } + .map(filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers) .eraseToAnyPublisher() } + + /// 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 + } } diff --git a/XcodesTests/AppStateUpdateTests.swift b/XcodesTests/AppStateUpdateTests.swift index a640c90..b0c668c 100644 --- a/XcodesTests/AppStateUpdateTests.swift +++ b/XcodesTests/AppStateUpdateTests.swift @@ -106,4 +106,14 @@ class AppStateUpdateTests: XCTestCase { XCTAssertEqual(subject.allXcodes.map(\.version), [Version("1.2.3")!, Version("0.0.0+ABC123")!]) } + + 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")]) + } }