SwiftUI: Loading Data Asynchronously
In our latest Swift Talk episode, Asynchronous Networking with SwiftUI, we show a technique for loading data on demand using the Tiny Networking library from previous episodes.
We start by creating an endpoint for the network resource we're interested in. The endpoint combines the URL of the resource with a parse function, which knows how to process the result:
struct User: Codable {
var name: String
var location: String?
}
func userInfo(login: String) -> Endpoint<User> {
return Endpoint(json: .get, url: URL(string: "https://api.github.com/users/\(login)")!)
}
let sample = userInfo(login: "objcio")
To drive SwiftUI updates with the results of network requests, we create a bindable object, Resource
, based on an endpoint. A bindable object has a single requirement: it needs to provide a didChange
property that SwiftUI will use to observe any changes. The implementation of Resource
starts the network request and calls didChange.send
once the data is available:
final class Resource<A>: BindableObject {
let didChange = PassthroughSubject<A?, Never>()
let endpoint: Endpoint<A>
var value: A? {
didSet {
DispatchQueue.main.async {
self.didChange.send(self.value)
}
}
}
init(endpoint: Endpoint<A>) {
self.endpoint = endpoint
reload()
}
func reload() {
URLSession.shared.load(endpoint) { result in
self.value = try? result.get()
}
}
}
The Resource
class is generic, it can be used with any endpoint. In our example, we'll use it to load user information from GitHub. The view below displays a loading text by default, but once the data has loaded SwiftUI will re-render the view and display the user's name and location:
struct ContentView : View {
@ObjectBinding var user = Resource(endpoint: userInfo(login: "objcio"))
var body: some View {
Group {
if user.value == nil {
Text("Loading...")
} else {
VStack {
Text(user.value!.name).bold()
Text(user.value!.location ?? "")
}
}
}
}
}
Unfortunately, the function builder syntax that's used to write view expressions such as these doesn't allow if let
. Until it does, we have to write an explicit nil
check and force-unwrap in the else
branch.
SwiftUI is very new, and we still have much to learn. We'll be exploring the framework more in the coming weeks, and you can learn with us on Swift Talk — the first episode is public. 🙂