Blog

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. 🙂


  • See the Swift Talk Collection

Stay up-to-date with our newsletter or follow us on Twitter.

Back to the Blog

recent posts