Old programmer but new to Apple ecosystem. I want to use the most modern APIs and Swift techniques to fetch some data over https and get progress reports.
Most tutorials and examples I can find use techniques I think are now outmoded such as RunLoop
or a Semaphore. Or they're for GUI code where there's already a Delegate.
I was able to get this far, to fetch the data using async
/await
from inside a struct annotated with @main
that has a static
async
function.
It uses URLSession.shared.data(for:
so I don't have to supply a Delegate.
I'm not sure if there's a way to add progress reporting to this. Or more likely I need a Delegate. In that case I've been unable to find how to make a simple Delegate just for this minimal use case. Or maybe there's a different way that I haven't found out about yet?
import Foundation
func fetchData() async throws -> Data {
// this is a 1 megabyte text file, big enough for updates
let url = URL(string: "https://gist.github.com/khaykov/a6105154becce4c0530da38e723c2330/raw/41ab415ac41c93a198f7da5b47d604956157c5c3/gistfile1.txt")!
var request = URLRequest(url: url)
let (data, _) = try await URLSession.shared.data(for: request)
return data
}
@main
struct Main {
static func main() async throws {
do {
let data = try await fetchData()
let str = String(data: data, encoding: .utf8)
dump(str)
} catch {
print("** \(error.localizedDescription)")
}
}
}
In an async/await
environment a possible solution is a Continuation
to have access to the data task and to be able to observe the progress
property of the task.
Replace fetchData
with
func fetchData() async throws -> Data {
var observation: NSKeyValueObservation?
// this is a 1 megabyte text file, big enough for updates
let url = URL(string: "https://gist.github.com/khaykov/a6105154becce4c0530da38e723c2330/raw/41ab415ac41c93a198f7da5b47d604956157c5c3/gistfile1.txt")!
return try await withCheckedThrowingContinuation { continuation in
let _ = observation // to silence a warning
let task = URLSession.shared.dataTask(with: url) { data, _, error in
if let error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: data!)
}
}
observation = task.progress.observe(\.fractionCompleted) { progress, _ in
print("progress: ", progress.fractionCompleted)
}
task.resume()
}
}