@@ -22,17 +22,26 @@ struct DownloadOfflineResourcesView: View {
2222 /// The action to dismiss the view.
2323 @Environment ( \. dismiss) private var dismiss
2424
25+ /// The progress of a download-all operation.
26+ @State private var downloadAllProgress : Progress ?
27+
2528 /// The view models for the on-demand resource requests.
2629 @State private var onDemandResources : [ String : OnDemandResource ] = [ : ]
2730
28- /// The current state of the "download all on-demand resources" request.
29- @State private var downloadAllRequestState : OnDemandResource . RequestState = . notStarted
30-
3131 /// A Boolean value indicating whether all of the `onDemandResources` have successfully downloaded.
32- private var allResourcesAreDownloaded : Bool {
33- guard !onDemandResources. isEmpty else { return false }
34- return onDemandResources. values
35- . allSatisfy { $0. requestState == . downloaded }
32+ private var allResourcesAreDownloaded : Bool { uniqueRequestStates == [ . downloaded] }
33+
34+ /// A Boolean value indicating whether there is an on going download-all operation.
35+ private var isDownloadingAll : Bool { downloadAllProgress != nil }
36+
37+ /// A Boolean value indicating whether there is an in progress download.
38+ private var isDownloadingResource : Bool { uniqueRequestStates. contains ( . inProgress) }
39+
40+ /// The distinct request states of all on-demand resources.
41+ private var uniqueRequestStates : Set < OnDemandResource . RequestState > {
42+ onDemandResources. values. reduce ( into: Set ( ) ) { result, resource in
43+ result. insert ( resource. requestState)
44+ }
3645 }
3746
3847 /// Returns the on-demand resource for the given sample.
@@ -45,24 +54,24 @@ struct DownloadOfflineResourcesView: View {
4554 Form {
4655 Section {
4756 Button {
48- downloadAllRequestState = . inProgress
57+ downloadAllProgress = Progress ( )
4958 } label: {
5059 Label {
5160 Text ( " Download All " )
5261 } icon: {
53- switch downloadAllRequestState {
54- case . inProgress :
55- ProgressView ( )
56- case . downloaded :
62+ if let downloadAllProgress {
63+ ProgressView ( downloadAllProgress )
64+ . progressViewStyle ( GaugeProgressViewStyle ( ) )
65+ } else if allResourcesAreDownloaded {
5766 Image ( systemName: " checkmark.circle " )
5867 . foregroundStyle ( . secondary)
59- default :
68+ } else {
6069 Image ( systemName: " arrow.down.circle " )
6170 }
6271 }
6372 . frame ( maxWidth: . infinity)
6473 }
65- . disabled ( onDemandResources. isEmpty || downloadAllRequestState != . notStarted || allResourcesAreDownloaded)
74+ . disabled ( onDemandResources. isEmpty || isDownloadingAll || allResourcesAreDownloaded)
6675 }
6776 Section {
6877 List ( samples, id: \. name) { sample in
@@ -78,7 +87,7 @@ struct DownloadOfflineResourcesView: View {
7887 . navigationBarTitleDisplayMode ( . inline)
7988 . toolbar {
8089 ToolbarItem ( placement: . confirmationAction) {
81- if onDemandResources . values . contains ( where : { $0 . requestState == . inProgress } ) {
90+ if isDownloadingResource {
8291 Button ( " Cancel " ) {
8392 for resource in onDemandResources. values where resource. requestState == . inProgress {
8493 resource. cancel ( )
@@ -108,13 +117,13 @@ struct DownloadOfflineResourcesView: View {
108117 return resources
109118 }
110119 }
111- . task ( id: downloadAllRequestState ) {
112- guard downloadAllRequestState == . inProgress else { return }
120+ . task ( id: isDownloadingAll ) {
121+ guard isDownloadingAll else { return }
113122 await downloadAll ( )
114123 }
115- . onChange ( of: allResourcesAreDownloaded ) {
116- guard allResourcesAreDownloaded else { return }
117- downloadAllRequestState = . downloaded
124+ . onChange ( of: isDownloadingResource ) {
125+ guard isDownloadingAll , !isDownloadingResource else { return }
126+ downloadAllProgress = nil
118127 }
119128 }
120129 }
@@ -123,8 +132,13 @@ struct DownloadOfflineResourcesView: View {
123132 /// - Note: The system may purge the resources at any time after the request object is deallocated.
124133 private func downloadAll( ) async {
125134 await withTaskGroup { group in
126- for resource in onDemandResources. values where resource. isDownloadable {
127- group. addTask ( operation: resource. download)
135+ for resource in onDemandResources. values {
136+ if resource. isDownloadable {
137+ group. addTask ( operation: resource. download)
138+ downloadAllProgress? . addChildUnits ( resource. progress)
139+ } else if resource. requestState == . inProgress {
140+ downloadAllProgress? . addChildUnits ( resource. progress)
141+ }
128142 }
129143 }
130144
@@ -197,3 +211,12 @@ private struct GaugeProgressViewStyle: ProgressViewStyle {
197211 }
198212 }
199213}
214+
215+ private extension Progress {
216+ /// Adds a process object and its unit count as a suboperation of a progress tree.
217+ /// - Parameter progress: The progress instance to add to the progress tree.
218+ func addChildUnits( _ progress: Progress ) {
219+ addChild ( progress, withPendingUnitCount: progress. totalUnitCount)
220+ totalUnitCount += progress. totalUnitCount
221+ }
222+ }
0 commit comments