This is the second part of my collection of articles on making a real-world application only using SwiftUI. Part 1 is an introduction to data flow and Redux, and an overview of the application — this is an important read if you want to fully understand this article.

This post details how to fetch data from an API and display remote images in SwiftUI.

Since MovieSwiftUI relies on the TMDb API to display information about movies, fetching remote data is a key point. While users’ custom movies list, wishlist and seenlist, settings, etc. are handled and saved locally, other information is fetched remotely.

Async Data

There isn’t much to say about SwiftUI in this part since I’m using the Redux pattern. Fetched data are basically reduced into my app state and then the state is published, so views are updated with the new data. But how does this really work? Let’s take a look.

I made a very simple APIService class that uses URLSession to make requests. All my models conform to Codable and are almost a one-to-one representation of models from the TMDb API. That way I don’t need custom decoders or transient models for views. There are a few places in the application where I use pure view models, but for the most part, the response is serialized in the application state in a logical way (like an in-memory database if you prefer) by the reducers.

Extract from my very basic HTTP client, full code on the MovieSwiftUI repository.

Then I have AsyncAction, which are Redux actions you can dispatch as usual, however, they’re not reduced by reducers (they could in order to provide a loading state for example), but they’re executed by middleware to trigger the request they implement in their execute() function. Once the request is finished, they chain another action to update the state from the data in contained in the response.

The actions:

The middleware is provided by my SwiftUIFlux Swift Package. Trigger the execute function from the AsyncAction as it’s dispatched.

Finally, the reducer adds the movie into the state movies property.

So what about the views? Let’s take a look at my MovieDetail view:

I retrieve my store using EnvironmentObject — accessible because it’s injected in the root view of my application. I then fetch the data I need when the .onAppear() of the views is triggered (Chris Eidhof has another approach where he triggers the resource load as SwiftUI subscribes to his ObservableObject). Because I map properties from my state into views computed properties, anything in the movie object updated by an AsyncAction will be updated in my MovieDetail view and its various components.

I hope this part provides a clear overview of how I’m doing API calls and remote data loading into my SwiftUI application. Now we’ll dig a bit more into SwiftUI, with the Image component provided by Apple, and how to make it work seamlessly with remote images.