I'm trying to put together a magazine browsing app using SwiftUI, but ran into a snag when trying to create a download progress indicator. I can't seem to find a way to make the ProgressBar I made update from urlSession(didWriteData) in URLSessionDownloadDelegate.
I've got a single DownloadManager which is passed to the main view as an environment object, and which handles the downloading for all magazine issues.
class DownloadManager: NSObject, URLSessionDownloadDelegate, ObservableObject {
var activeDownloads: [URL: Download] = [:]
lazy var downloadSession: URLSession = {
let config = URLSessionConfiguration.default
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
/*downloading and saving the issues is handled here*/
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
guard
let url = downloadTask.originalRequest?.url,
let download = activeDownloads[url]
else {
return
}
//this is the value that needs to be indicated by the UI
download.progress = (Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
}
}
The custom Download class contains the following:
class Download {
var issue: Issue
var url: URL
var progress: Float = 0.0
var task: URLSessionDownloadTask?
var resumeData: Data?
init(issue: Issue) {
self.issue = issue
self.url = issue.webLocation
}
}
The available issues to download are displayed in rows in a List, and while they're being downloaded the progress is supposed to be displayed by the ProgressBar. The relevant portion of the row's layout code is here:
struct IssueRow: View {
var issue: Issue
@EnvironmentObject var downloadManager: DownloadManager
@State var downloadProgress: Float = 0.0
var body: some View {
//the progress bar that needs to continuously update
ProgressBar(value: $downloadProgress)
.frame(height: 4)
}
private func downloadIssue() {
downloadManager.startDownload(issue: issue)
downloadProgress = downloadManager.activeDownloads[issue.webLocation]!.progress
//This does not actually change as the download progresses
}
Inside ProgressBar, the progress is stored as a @Binding float.
struct ProgressBar: View {
@Binding var value: Float
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .leading) {
Rectangle().frame(width: geometry.size.width, height:
geometry.size.height)
.opacity(0.3)
.foregroundColor(Color(UIColor.systemTeal))
Rectangle().frame(width: CGFloat(self.value)*geometry.size.width, height: geometry.size.height)
.foregroundColor(Color.navy)
.animation(.linear)
}
}
}
}
I'm really not sure how to synchronize the behavior of SwiftUI and its various elements with the older stuff in Swift. Any help is greatly appreciated!