State-based reactive SwiftUI views using switch statements

depicting a fire alarm switch as an analogy for switch statements and their importance in swift

Overview

You’ve started writing a complex SwiftUI app, and you’ve realized that you need to display a view to the user based on the state of the view model. Seems, pretty easy and harmless. Well, it is! But, previously to the Swift 5.3 release, to do this, you had to use redundant if, else if, else statements for each of the different states. Not only does the view of the robust code make me want to barf, but I got so tired of writing the boiler plate conditional for each state that the view can be in.

Thankfully, this has all changed with the addition of switch statements being allowed inside of function builders! Throughout this blog, I’m going to be talking about an example where a user is presented with a form, and how we can use the new switch ability to make our lives easier. Let’s jump right into it!

Creating a view model

Before we can do anything with our form for our users, we need to first create the View Model that establishes the state for our form to react to. To do this, I’m going to create a view model as an observable object that has a property called state. The state will be an enum of type <viewmodel>.State, where each case is a different state in which the view can be in. For this example, our three states will be: completedForm, form, and loading.

class ViewModel: ObservableObject {
    @Published var state: State = .form
    
    // some code to update state...
}

extension ViewModel {
    enum State {
        case form, compeltedForm, loading
    }
}

Since state is a published property, combine under the hood will tell the views that observe this object to re-render the view. This re-rendering will be important for the next part since we want to update the view based on the state!


The old way of doing it 🤢

Now before I show you the beauty of using switch statements in function builders, let’s take a look at how we would’ve had to handle our views before. To get a reactive view, we first create an instance of the view model as an @ObservedObject. This will then tell our view to re-render whenever the state variable changes. We then want to change the view displayed to the user based on the state of the viewModel.

struct MyOldView: View {
    @ObservedObject var vm = ViewModel()
    
    var body: some View {
        VStack {
            if self.vm.state == .form {
                LoadingView()
            } else if self.vm.state == .completedForm {
                CompletedForm()
            } else if self.vm.state == .loading {
                LoadingView()
            }
        }
    }
}

As you can see, for every single case for the state you have to manually compare the current state to that case 🤮 🤮 🤮 . It just looks awful, and not very swifty at all. Furthermore, you don’t even know if you’ve hit all of the states.

For instance, another programmer on your team edited your view model and now there is a new state that the view can be in, but, the view wasn’t updated to support that state. There are no build issues because, in swift eyes, you’ve done everything right. But, in reality, your user is about to get a blank view when they get to that state. YIKES!

Let’s see how we can handle this better with switch statements!


“Switch” the way we do things

Instead of using if statements, we’re going to refactor our old view by ripping out all of the gross conditionals and place in a nice switch statement. To me, removing the conditionals is kind of like throwing away an old pair of running shoes and getting a new pair. Sure, the old ones worked, but they just didn’t look good and didn’t make you feel good while using them.

“Alright Brady, shut up and show me the dang switch!”

all of you reading this
struct MyView: View {
    @ObservedObject var vm = ViewModel()

    var body: some View {
        VStack {
            switch self.vm.state {
            case .loading:
                LoadingView()
            case .form:
                UserForm()
            case .completedForm:
                CompletedForm()
            }
        }
    }
}

Now, since we’re using a switch over an enum, we will also get build time fails whenever we don’t include all of the cases (or give a default). This little feature will give us protection from giving the user a blank view and will also hopefully keep our app rating from the dreaded 1-star review.


Wrap Up

As we’ve seen, utilizing switch statements for reactive view management can be both beautiful 💁‍♀️ and convenient for SWE “best practices”. And if you’re new to reacting views to states, I hope you learned a thing or two about view models and rendering a view based on the state of the view model.

If you liked today’s content, make sure you subscribe to the newsletter down below! As always, thanks for taking the time to unwrap some bytes with me. Cheers! 🍻

Processing…
Success! You're on the list.

shoutout to Dave Phillips on Unsplash for the awesome photo!

Leave A Comment