@@ -33,7 +33,7 @@ final class OnDemandResource {
3333 }
3434
3535 /// The progress of the on-demand resource request.
36- var progress : Progress { request . progress }
36+ let progress = Progress ( totalUnitCount : 100 )
3737
3838 /// A Boolean value indicating whether a resource request can be initiated.
3939 var isDownloadable : Bool {
@@ -49,6 +49,11 @@ final class OnDemandResource {
4949 /// The on-demand resource request.
5050 private var request : NSBundleResourceRequest
5151
52+ /// A task for monitoring `request.progress` to update `self.progress`.
53+ ///
54+ /// This is needed because passing `request.progress` to a `ProgressView` can cause race condition crashes.
55+ @ObservationIgnored private var progressTask : Task < Void , Never > ?
56+
5257 /// Initializes a request with a set of Resource Tags.
5358 init ( tags: Set < String > ) async {
5459 let request = NSBundleResourceRequest ( tags: tags)
@@ -59,9 +64,13 @@ final class OnDemandResource {
5964 requestState = isResourceAvailable ? . downloaded : . notStarted
6065 }
6166
67+ deinit {
68+ progressTask? . cancel ( )
69+ }
70+
6271 /// Cancels the on-demand resource request.
6372 func cancel( ) {
64- progress . cancel ( )
73+ progressTask ? . cancel ( )
6574 request. endAccessingResources ( )
6675 requestState = . cancelled
6776 }
@@ -79,6 +88,27 @@ final class OnDemandResource {
7988 error = nil
8089 }
8190
91+ // Monitors `request.progress` to update `self.progress`.
92+ progressTask = Task { @MainActor [ weak self] in
93+ guard let self else { return }
94+
95+ // A stream is used here because `progress.publisher` doesn't always produce values.
96+ let stream = AsyncStream { continuation in
97+ let observation = request. progress
98+ . observe ( \. fractionCompleted, options: [ . initial, . new] ) { _, change in
99+ guard let newValue = change. newValue else { return }
100+ continuation. yield ( newValue)
101+ }
102+ continuation. onTermination = { _ in
103+ observation. invalidate ( )
104+ }
105+ }
106+
107+ for await fractionCompleted in stream {
108+ self . progress. completedUnitCount = Int64 ( fractionCompleted * 100 )
109+ }
110+ }
111+
82112 do {
83113 requestState = . inProgress
84114 try await request. beginAccessingResources ( )
@@ -91,6 +121,8 @@ final class OnDemandResource {
91121 cancel ( )
92122 }
93123 }
124+
125+ progressTask? . cancel ( )
94126 }
95127}
96128
0 commit comments