Your First iOS & SwiftUI App: An App from Scratch

Feb 13 2023 · Swift 5.7, iOS 16, Xcode 14

Part 2: SwiftUI Data

13. SwiftUI State

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 12. Buttons & Actions Next episode: 14. SwiftUI Bindings

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 13. SwiftUI State

A key part of programming SwiftUI is state. Rather than start with the computer science definition of state, let’s go with something that might be a little more familiar: the dashboard of a car.

The most noticeable parts of a car dashboard are probably its gauges and odometers. They show the car’s current speed, fuel level, distance traveled, and so on, each of which is some kind of numeric quantity.

Dashboards also have warning lights, such as the low oil warning light, or the the “it’s time to take the car to the shop for some overpriced maintenance” light. Each of these lights is either on, indicating that there’s a problem that needs the driver’s attention, or off. This “on/off”, “true/false” information can be described as a boolean value.

So the information on a car’s dashboard — such as speed, fuel level, whether or not the car needs maintenance — taken all together, is a visualization of the car’s state.

Keep in mind that the dashboard isn’t the car’s actual state - it’s just a visualization of it.

To see what I mean, think about what happens when the driver changes the car’s state. For example, the driver presses the accelerator, and the car starts moving faster. The dashboard then updates to show the new speed. So the car’s state is how fast the car is actually moving - and the dashboard is just helping the driver visualize that fact.

Internal circumstances can also change the car’s state. For example, as you drive the car burns gas. The car’s state is how much gas is in the gas tank, and the dashboard hopefully updates the fuel indicator.

But what happens if the car’s state and dashboard aren’t in synch? This actually happened to me with my first car. The fuel gauge was always at empty. And it was a big problem! I never knew how much gas I had.

It turns out that this type of mistake is quite common while developing an app. That is, your user interface might not accurately represent the internal state of your app. You may have sometimes come across an app with a bug like this - for example, an app says you have 5 new messages, but when you check you actually have a different amount.

One of the nice things about SwiftUI, is that you’re forced to develop your apps in such a way that your user interfaces and your state are always consistent, which prevents these frustrating types of bugs.

At this point the app state we’re trying to add to Bullseye is very simple: either the popup alert is visible, or it’s not. This is a boolean value, which means it’s either true, or false.

Do you remember how a few episodes ago, you learned that class and struct templates, and the instances of those templates, have data and functionality?

Well, the boolean value to keep track of whether the popup alert is visible or not is an example of some data that we need to keep track of on our ContentView struct and instance. Let’s see how we can do this.

We’re working in contentview.swift again. I’m going to close the navigator, and show the canvas again.

And start that up so we can see the preview.

Now! Right after this line it says struct content view colon view, and before this line that says var body some view. We’re going to add a new property, that’s going to store whether or not alert is visible.

So the way we’re going to do this, is we’re going to start by typing @State. @State is a special keyword that says this is a state variable.

And it means whenever a state variable changes, SwiftUI to automatically recompute the body, resolve the body of the view, and so the view and state variable are always in sync.

So next, we’re going to put the word private, which is kind of a best practice when you’re making a state variable to say that this variable is private to content view and other objects or other structures, should not be able to access it.

Next we’re going to put the keyword var for variable, which means this can change.

And finally we’re going to put the name of the variable, alert is visible.

When you’re making variables like this, it’s a good practice to use camel casing, which is a fancy way of saying you start lowercase, and every time you need a new word, you have the first character of that new word capitalized, just kind of a coding style thing with Swift.

At the end we need to tell Swift what type of variable this is.

So the variable that we’re going to be using here is a boolean variable, which means it can be one of two things, either true or false.

And finally, we’ll set an initial value. Remember that when the app starts up, the alert should not be visible. It’s only when you tap Hit Me that the alert should be visible. So, we want this variable to start as false

Put space equals, and then the initial value, false.

@State private var alertIsVisible: Bool = false

Now that the variable is set up, and it’s set to be false when the app starts up, we want to make the button update that variable when it’s pressed.

So, down in the Button’s code, where the print statement currently is, this is code that gets run when the user taps the Hit Me button.

So this is exactly when we want to set alertIsVisible to true.

We can remove the print statement, and then changing the variable is as simple as typing alertIsVisible, then the equals sign, and then true.

alertIsVisible = true

Remember that in SwiftUI, you’re forced to develop your apps in such a way that your user interfaces and your state are always consistent.

To do this, you keep track of your app’s state using variables marked with @State. Each state variable will have an initial value - for example, we set alertIsVisible to false.

Then when the app starts up, iOS calls body to get a “dashboard” based on the current app state. For example, currently the body method returns the basic Bullseye user interface, but doesn’t show a popup. So far, so good.

But what happens when you set alertIsVisible to true? That is changing the app’s state, so it’s important that the user interface is updated to be consistent.

Well, since you already marked that variable with @State, iOS will automatically refresh the body. So it’s your job to make sure that body takes the app state into consideration, and displays an alert popup if alertIsVisible is true.

Let’s see how we can do this.

All right. So next up, what we’re going to do, is still related to the button.

This bit of code runs when the button is tapped. After this final curly brace, this is completing the definition of the button here.

I want to call a method on the resulting button. So right after this line, hit enter, and then type in dot alert.

So we’re calling a method on the button we just created. There are many similar methods with this name. The one we want has four arguments: titleKey, isPresented, actions, and message.

I’m going to put each of these on its own line, and the closing parens on its own line at the end.

  .alert(
    <titleKey: ...>,
    isPresented: Binding<...>,
    actions: <...>,
    message: <...>
  )

Now we can fill each of these in.

The title will appear at the top of the alert, and we can just enter whatever we want it to say in quote marks. It’s effectively making a Text view for us with this.

  .alert(
    "Hello there!",

Next one, isPresented wants us to pass in a variable that keeps track of whether the alert is currently visible or not.

Well, good news, we already have such a property called alertIsVisible.

But there’s one thing, we have to put $ sign before this. And this is because we want to convert our state variable to a binding into the state variable.

And you’ll learn more about what bindings are in the next episode. But for now, just know that we need to put $ sign before the variable we want to use to trigger this alert.

  .alert(
    "Hello there!",
    isPresented: $alertIsVisible,
    actions: <...>,
    message: <...>
  )

The third part is asking us for Actions. This is a bit misleading, because you might think this is like the button’s action. But what it’s really asking for is for you to put any buttons in here you want to appear in the alert. Any button you put here will close the alert by default without you having to code anything.

If you don’t put anything in here at all, and just use a pair of empty curly braces, SwiftUI will create a button for you that just says “OK” on it.

If you want that button to do or say something else, you can add it just like you would anywhere else. Which means we can copy the button we already have, paste it in here, and make it say something different…

And in the action, again, it will close the alert no matter what we do here, so for now let’s just print a message like “Alert closed”.

actions: {
  Button("Awesome!") {
    print("Alert closed")
  }
}

The final bit is a message. This is any other views you want to include in the alert to describe what it’s about. This part isn’t technically required for the alert to work, but let’s put something in there.

We already know the syntax we’re to make text, right? We put text and two parenthesis, we put what we want to say in quotes. How about “This is my first alert!”.

    message: {
      Text("This is my first alert!")
    }
  )

With this alert finished up we can test the whole thing out. We don’t even need to build to the simulator, we can try it out right here in the canvas.

Make sure it’s in live mode, with the little play button highlighted in the lower left of the canvas.

And click on the “Hit me” button.

The alert will pop up. And there’s the “Hello there” title, and our message “this is my first alert, and then the custom button that says “Awesome”.

Click the awesome button, and the alert will close, and we’re back to the main screen of the app!

Remember that the print message isn’t appearing in the console because we’re using the canvas.

So if something seems to be going wrong in the canvas interactions, always check the app running in a simulator, then you can see any print statements you’ve set up.

  .alert(
    "Hello there!",
    isPresented: $alertIsVisible,
    actions: {
      Button("Awesome!") {
        print("Alert closed")
      }
    },
    message: {
      Text("This is my first alert")
    }
  )

Congratulations, you’ve finally made your app interactive! What you just did may have seemed like gibberish to you, but that shouldn’t matter. We’ll take it one small step at a time.

You can now strike off the next item from your programming to-do list: Show a popup when the user taps the Hit Me button.

Take a little break, let it all sink in and come back when you’re ready for more! You’re only just getting started.