SwiftUI Tip: Enumerating a View State

Due to its declarative and reactive nature, SwiftUI works pretty well with this technique. Suppose we are building a screen that fetches a Github profile. We begin by describing the possible stages of this fetch process:

enum State: Equatable {
    case `default`
    case loading
    case fetched(profile: GithubProfileViewModel)
    case failure(error: HttpError)
}

In the view model, we declare a variable holding the state:

@MainActor
final class GithubProfileFetchViewModel: ObservableObject {

    @Published
    private(set) var state = State.default

    func fetchProfile(using username: String) async {
        // This method will update the state in the different stages
        // of the fetch process. Notice the state is published.
    }

    // ...
}

Then in the View, we use this state when declaring our body:

struct GithubProfileFetchView: View {

    @StateObject
    private var viewModel = GithubProfileFetchViewModel()

    // ...

    var body: some View {
        VStack {
            // ...
            
            switch viewModel.state {
            case .`default`:
                DefaultProfileView()
                
            case .fetched(let profileViewModel):
                ScrollView {
                    GithubProfileView(viewModel: profileViewModel)
                        .padding()
                }
                
            case .loading:
                LoadingIndicator()
                
            case .failure(let error):
                if error == .requestFailed(statusCode: 404) {
                    ErrorView.profileNotFound()
                } else {
                    ErrorView.connectionError()
                }
            }

            // ...
        }
        .task(id: shouldStartFetch) {
            await fetchProfile()
        }
    }
    // ...
}

The usage of switch makes the code really readable. Whenever the state changes, SwiftUI presents a different view for us. Since SwiftUI is declarative, we don’t need to setup bindings, and manually change what we display. This process is all done automatically for us.

Note that this technique works with other architectures too, and with other property wrappers (e.g @State, @ObservableObject, @EnvironmentObject). You can check the full view model or view code.

You might also want to read this article by Apple.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s