From eb66254fcfcfde3ec03d3f79c955dae6f631fce4 Mon Sep 17 00:00:00 2001 From: Tyler Smith Date: Mon, 8 Jun 2026 23:56:07 -0400 Subject: [PATCH] Avoid synchronous installed Xcode discovery during launch --- Xcodes/Backend/AppState+Install.swift | 4 +++- Xcodes/Backend/AppState+Update.swift | 2 ++ Xcodes/Backend/AppState.swift | 26 ++++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Xcodes/Backend/AppState+Install.swift b/Xcodes/Backend/AppState+Install.swift index a116556e..5cc2891b 100644 --- a/Xcodes/Backend/AppState+Install.swift +++ b/Xcodes/Backend/AppState+Install.swift @@ -77,13 +77,15 @@ extension AppState { } private func getXcodeArchiveAsync(_ installationType: InstallationType, downloader: Downloader) async throws -> (AvailableXcode, URL) { + let installedXcodes = await updateInstalledXcodesAsync(recomposeAllXcodes: false) + switch installationType { case .version(let availableXcode): let resolution = try mapInstallResolutionError { try XcodeInstallResolutionService().resolve( .availableXcode(availableXcode), availableXcodes: [], - installedXcodes: Current.files.installedXcodes(Path.installDirectory), + installedXcodes: installedXcodes, willInstall: true ) } diff --git a/Xcodes/Backend/AppState+Update.swift b/Xcodes/Backend/AppState+Update.swift index ab9ea3d8..cd2952c9 100644 --- a/Xcodes/Backend/AppState+Update.swift +++ b/Xcodes/Backend/AppState+Update.swift @@ -26,6 +26,7 @@ extension AppState { updateTaskID = nil } } + await self.updateInstalledXcodesAsync() await self.updateSelectedXcodePathAsync() } updateTask = task @@ -49,6 +50,7 @@ extension AppState { } } do { + await self.updateInstalledXcodesAsync() await self.updateSelectedXcodePathAsync() let xcodes = try await self.updateAvailableXcodes(from: self.dataSource) try Task.checkCancellation() diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 3d432196..c1da7538 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -48,7 +48,7 @@ class AppState: ObservableObject { } updateAllXcodes( availableXcodes: newValue, - installedXcodes: Current.files.installedXcodes(Path.installDirectory), + installedXcodes: installedXcodes, selectedXcodePath: selectedXcodePath ) } @@ -61,11 +61,12 @@ class AppState: ObservableObject { willSet { updateAllXcodes( availableXcodes: availableXcodes, - installedXcodes: Current.files.installedXcodes(Path.installDirectory), + installedXcodes: installedXcodes, selectedXcodePath: newValue ) } } + private var installedXcodes: [InstalledXcode] = [] @Published var updateTask: Task? var updateTaskID: UUID? var isUpdating: Bool { updateTask != nil } @@ -228,6 +229,9 @@ class AppState: ObservableObject { guard !isTesting else { return } try? loadCachedAvailableXcodes() try? loadCacheDownloadableRuntimes() + Task { @MainActor in + await updateInstalledXcodesAsync() + } helperStatusTask = Task { @MainActor in await checkIfHelperIsInstalled() helperStatusTask = nil @@ -855,6 +859,24 @@ class AppState: ObservableObject { } } + @discardableResult + func updateInstalledXcodesAsync(recomposeAllXcodes: Bool = true) async -> [InstalledXcode] { + let installDirectory = Path.installDirectory + let files = Current.files + let installedXcodes = await Task.detached(priority: .userInitiated) { + files.installedXcodes(installDirectory) + }.value + + self.installedXcodes = installedXcodes + if recomposeAllXcodes { + updateAllXcodes( + availableXcodes: availableXcodes, + installedXcodes: installedXcodes, + selectedXcodePath: selectedXcodePath + ) + } + return installedXcodes + } // MARK: - Private