URL Image view in SwiftUI

Dmytro Anokhin
Jun 7 · 3 min read

Views that download image from URL are extremely popular in iOS development. It is very convenient to set a URL on a view and get image displayed. And not to worry about networking, caching, and synchronization.

This post describes how to build a view that can download an image from URL behind the scenes using SwiftUI. Code is available on my github: url-image and url-image-app.

View

The first step would be to declare a new view. I name it URLImage. Views in SwiftUI are structs conforming to View protocol and so URLImage is.

URLImage can be initialized with URL and optional placeholder image.

To manage downloading process I decided to create a separate ImageLoader object. This object also provides access to the downloaded image and serves as a source of true for URLImage.

Implementation of body method is a simple if/else condition to show downloaded image or a placeholder.

Now when to start loading? SwiftUI provides appearance callbacks with onAppear and onDisappear modifiers. Using this modifiers enables placeholder appearance to start loading process, and cancel loading when it disappears.

Note: downloaded image has same statements but empty. This is because modifier type wraps a view type. Statement image.onAppear {} returns _ModifiedContent<Image, _AppearanceActionModifier> type and onDisappear wrapper it once more. We need both return statements to have the same type. This is why both branches must have same modifiers. Otherwise I will have to return type erased AnyView.

Image loading and binding

Alright, back to the ImageLoader object. ImageLoader uses SwiftUI bindings to trigger UI updates.

To implement a binding ImageLoader must confirm to BindableObject protocol. This way it can notify about changes. Bindable objects can notify subscribers using didChange property.

URLImage must declare a property with @ObjectBinding for SwiftUI engine to respond to changes and update UI.

Note: for simpler views it is enough to use @State attribute. SwiftUI limits using properties marked as @State from outside the body definition. @ObjectBinding is used for external dependencies. In my case URL is external dependency.

Internally ImageLoader is a state machine defined by LoadingState enum. When state changes UI must update. This is done using didChange.send() in the property observer.

This is pretty much it for the SwiftUI part. ImageLoader has some sophisticated loading logic and uses local and in-memory caches. But this is not related to SwiftUI.

Example

Let’s have a look how we can use URLImage. I used https://picsum.photos API for some test images.

In this example URLImage used to displayed a list of images. Here’s how the result looks:

You can use URLImage the same way and with same modifiers as Image in SwiftUI.


Some thoughts on SwiftUI. It is impressive how little code is actual view related. And how seamlessly custom view integrates with SwiftUI. Much more code is needed to load images and manage local/in-memory caches. I plan to also explore new Combine framework and see if I can reduce and streamline that part.

I feel that URLImage fits declarative form of SwiftUI pretty good.

URLImage is a package available on github: url-image. You can also find a demo app here: url-image-app.

Hope you find it interesting to read as it is interesting for me to explore.

Cheers and till next time.

Dmytro Anokhin

Written by

iOS Developer, here to share best practices learned through my experience. You can find me on Twitter: https://twitter.com/dmytroanokhin

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade