Handling Progress
For long-running asynchronous tasks like file uploads or downloads, providing progress updates to the user is essential. Then
has built-in support for reporting and handling progress.
Reporting Progress
To enable progress reporting, you use the Promise
initializer that includes a progress
closure. This closure can be called as many times as needed to report the current progress as a Float
value between 0.0
and 1.0
.
Here is an example of a function that simulates a file upload and reports its progress.
func uploadAvatar(_ image: UIImage) -> Promise<URL> {
return Promise { resolve, reject, progress in
print("Starting upload...")
// Simulate progress updates
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
progress(0.25)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
progress(0.5)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
progress(0.75)
}
// Simulate completion
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
let url = URL(string: "https://example.com/avatar.png")!
resolve(url)
}
}
}
Consuming Progress Updates
To listen for progress updates, you can chain the .progress()
method onto your promise. The block you provide will be called every time the promise reports a new progress value.
The .progress()
method is chainable and does not interfere with .then()
or .onError()
.
uploadAvatar(myImage)
.progress { p in
// Update a UIProgressView or print to console
print("Upload progress: \(p * 100)%")
}
.then { url in
print("Upload complete! URL: \(url)")
}
.onError { error in
print("Upload failed: \(error)")
}
Output:
Starting upload...
Upload progress: 25.0%
Upload progress: 50.0%
Upload progress: 75.0%
Upload complete! URL: https://example.com/avatar.png
Progress blocks will be called on the same thread that the progress
closure was called on inside the promise body.