Skip to content

Commit 667783b

Browse files
committed
Fix ProgressView crash.
1 parent 34831da commit 667783b

File tree

1 file changed

+34
-2
lines changed

1 file changed

+34
-2
lines changed

Shared/Supporting Files/Models/OnDemandResource.swift

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)