Skip to content

Commit 622fac6

Browse files
committed
Set up overall progress object
to track both downloading and unarchiving and reflect this in dock progress
1 parent de35bed commit 622fac6

2 files changed

Lines changed: 46 additions & 10 deletions

File tree

Xcodes/Backend/AppState+Install.swift

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ extension AppState {
4444

4545
Logger.appState.info("Using \(downloader) downloader")
4646

47+
setupDockProgress()
48+
4749
return validateSession()
4850
.flatMap { _ in
4951
self.getXcodeArchive(installationType, downloader: downloader)
@@ -52,6 +54,8 @@ extension AppState {
5254
self.installArchivedXcode(xcode, at: url)
5355
}
5456
.catch { error -> AnyPublisher<InstalledXcode, Swift.Error> in
57+
self.resetDockProgressTracking()
58+
5559
switch error {
5660
case InstallationError.damagedXIP(let damagedXIPURL):
5761
guard attemptNumber < 1 else { return Fail(error: error).eraseToAnyPublisher() }
@@ -100,6 +104,7 @@ extension AppState {
100104
self.downloadOrUseExistingArchive(for: availableXcode, downloader: downloader, progressChanged: { [unowned self] progress in
101105
DispatchQueue.main.async {
102106
self.setInstallationStep(of: availableXcode.version, to: .downloading(progress: progress))
107+
self.overallProgress.addChild(progress, withPendingUnitCount: AppState.totalProgressUnits - AppState.unxipProgressWeight)
103108
}
104109
})
105110
.map { return (availableXcode, $0) }
@@ -152,7 +157,7 @@ extension AppState {
152157
cookies
153158
)
154159
progressChanged(progress)
155-
updateDockIcon(withProgress: progress)
160+
156161
return publisher
157162
.map { _ in destination.url }
158163
.eraseToAnyPublisher()
@@ -162,12 +167,12 @@ extension AppState {
162167
let resumeDataPath = Path.xcodesApplicationSupport/"Xcode-\(availableXcode.version).resumedata"
163168
let persistedResumeData = Current.files.contents(atPath: resumeDataPath.string)
164169

165-
return attemptResumableTask(maximumRetryCount: 3) { [weak self] resumeData -> AnyPublisher<URL, Error> in
170+
return attemptResumableTask(maximumRetryCount: 3) { resumeData -> AnyPublisher<URL, Error> in
166171
let (progress, publisher) = Current.network.downloadTask(with: availableXcode.url,
167172
to: destination.url,
168173
resumingWith: resumeData ?? persistedResumeData)
169174
progressChanged(progress)
170-
self?.updateDockIcon(withProgress: progress)
175+
171176
return publisher
172177
.map { $0.saveLocation }
173178
.eraseToAnyPublisher()
@@ -177,13 +182,11 @@ extension AppState {
177182
})
178183
.eraseToAnyPublisher()
179184
}
180-
181-
private func updateDockIcon(withProgress progress: Progress) {
182-
DockProgress.style = .bar
183-
DockProgress.progressInstance = progress
184-
}
185185

186186
public func installArchivedXcode(_ availableXcode: AvailableXcode, at archiveURL: URL) -> AnyPublisher<InstalledXcode, Error> {
187+
unxipProgress.completedUnitCount = 0
188+
overallProgress.addChild(unxipProgress, withPendingUnitCount: AppState.unxipProgressWeight)
189+
187190
do {
188191
let destinationURL = Path.installDirectory.join("Xcode-\(availableXcode.version.descriptionWithoutBuildMetadata).app").url
189192
switch archiveURL.pathExtension {
@@ -423,6 +426,9 @@ extension AppState {
423426
}
424427
self.presentedAlert = .privilegedHelper
425428
}
429+
430+
unxipProgress.completedUnitCount = AppState.totalProgressUnits
431+
resetDockProgressTracking()
426432

427433
return helperInstallConsentSubject
428434
.flatMap {
@@ -463,6 +469,24 @@ extension AppState {
463469
.eraseToAnyPublisher()
464470
}
465471

472+
// MARK: - Dock Progress Tracking
473+
474+
private func setupDockProgress() {
475+
DockProgress.progressInstance = nil
476+
DockProgress.style = .bar
477+
478+
let progress = Progress(totalUnitCount: AppState.totalProgressUnits)
479+
progress.kind = .file
480+
progress.fileOperationKind = .downloading
481+
overallProgress = progress
482+
483+
DockProgress.progressInstance = overallProgress
484+
}
485+
486+
func resetDockProgressTracking() {
487+
DockProgress.progress = 1 // Only way to completely remove overlay with DockProgress is setting progress to complete
488+
}
489+
466490
// MARK: -
467491

468492
func setInstallationStep(of version: Version, to step: InstallationStep) {

Xcodes/Backend/AppState.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,19 @@ class AppState: ObservableObject {
107107
private var selectPublisher: AnyCancellable?
108108
private var uninstallPublisher: AnyCancellable?
109109
private var autoInstallTimer: Timer?
110+
111+
// MARK: - Dock Progress Tracking
112+
113+
public static let totalProgressUnits = Int64(10)
114+
public static let unxipProgressWeight = Int64(1)
115+
var overallProgress = Progress()
116+
var unxipProgress = {
117+
let progress = Progress(totalUnitCount: totalProgressUnits)
118+
progress.kind = .file
119+
progress.fileOperationKind = .copying
120+
return progress
121+
}()
122+
110123
// MARK: -
111124

112125
var dataSource: DataSource {
@@ -491,8 +504,7 @@ class AppState: ObservableObject {
491504
// Cancel the publisher
492505
installationPublishers[id] = nil
493506

494-
// Remove dock icon progress indicator
495-
DockProgress.progress = 1 // Only way to completely remove overlay with DockProgress is setting progress to complete
507+
resetDockProgressTracking()
496508

497509
// If the download is cancelled by the user, clean up the download files that aria2 creates.
498510
// This isn't done as part of the publisher with handleEvents(receiveCancel:) because it shouldn't happen when e.g. the app quits.

0 commit comments

Comments
 (0)