Modern app development with SwiftUI and Combine eliminates a lot of boilerplate code. Tools like Playgrounds extend this further to allow quick prototyping. One common issue is to load arbitrary content (JSON or binary) from network and display it in a SwiftUI view. The other day I was looking for a simple yet elegant way to use in quick prototypes. I came up with a reusable view that can load arbitrary content. After a couple of iterations I discovered some interesting tricks to share.

Content Loading Design

Image for post

Content Loading stages: initial, in progress, success, and failure

Content loading is at least three stage process:

  • Before the loading starts there is the initial moment - after objects were created, and before a trigger to load the content;
  • The actual process of loading content. We need to present some progress or activity indication in the UI;
  • And finally the result, success or failure.

SwiftUI encourages creating small, reusable views, and use composition to create the complete picture. Each stage of the content loading process will require a view. The container view will compose the result.

The content loading process can be represented using an enum with associated values. Swift provides Result type, that could be used to represent completed process. However, I find it more convenient to represent the result as separate success and failure cases. This is due to added support of switch statement in function builders (Xcode 12).

It is easy to see what we are aiming for: the container view can switch over the loading state to provide a corresponding view.

enum RemoteContentLoadingState<Value> {

	    case initial

	    case inProgress

	    case success(_ value: Value)

	    case failure(_ error: Error)
	}

Loading Content in SwiftUI View

You probably know by now that a view in SwiftUI can not load content by itself. This is because SwiftUI views are value types. Content loading requires back and forth communication possible only with reference types.

The way to provide remote content to a SwiftUI view is by using ObservedObject property wrapper and ObservableObject protocol.

ObservableObject protocol synthesizes a publisher that emits before the object has changed.

Typically, you would create a class that confirms to ObservableObject and reference it using ObservedObject property wrapper from your view.

Because we are building a reusable view it makes sense to inject ObservableObject. To start, declare RemoteContent protocol.

protocol RemoteContent : ObservableObject {

	    associatedtype Value

	    var loadingState: RemoteContentLoadingState<Value> { get }

	    func load()

	    func cancel()
	}

#ios #apple #xcode #swiftui #swift

Building reusable content loading view with SwiftUI and Combine
7.25 GEEK