Waldo sessions now support scripting! – Learn more
App Development

How to Use Toggle in SwiftUI

Juan Reyes
Juan Reyes
How to Use Toggle in SwiftUI
January 10, 2023
13
min read

In this article, we'll explore how to create a toggle in SwiftUI. First, we'll explain what toggles are, how they work, and when they're the best choice for a UI element in your app. Then we'll go through how to successfully implement a simple, functioning toggle element in your app, customize it, and make it look great. Finally, we'll provide a simple test workflow that will allow you to trigger the toggle and check that it's working correctly.

If you're already familiar with SwiftUI, you can skip the following introduction. However, for those who may not be familiar with SwiftUI, we'll provide a brief overview before we proceed.

SwiftUI is a declarative framework for building user interfaces on Apple's platforms

SwiftUI: An Introduction

SwiftUI is a declarative framework for building user interfaces on Apple's platforms. It was introduced at the Worldwide Developers Conference (WWDC) in 2019 as a replacement for the aging UIKit framework.

SwiftUI is built on top of the Swift programming language, providing a new way to build user interfaces that are more concise and easier to understand than UIKit.

One of the key features of SwiftUI is its use of declarative syntax. With a declarative syntax, you describe the user interface you want to create, and the framework creates and updates the user interface for you. This means that you don't have to write as much code, and you can focus on the content and layout of your user interface rather than the mechanics of how it's created and updated.

SwiftUI also includes many built-in views and controls, such as text fields, buttons, and lists, which you can use to build your user interface. One such control is the toggle.

Toggles in SwiftUI

Toggles are a common UI element that allows users to switch between two mutually exclusive states, such as "on" and "off." They're often used to enable or disable a particular feature or option in an app.

For example, you might use a toggle to enable or disable an app's push notifications or switch between different display modes. A small oval button with a round dot inside typically represents a toggle. The user can move this dot to the left or right to change the current state.

In terms of functionality, toggles are similar to switches but are typically smaller and more compact. Developers and designers use toggles when there's limited space available for UI elements or when they want to provide a simple and straightforward way for users to enable or disable a particular feature.

How to Implement Toggle Elements in SwiftUI

The first step to creating a toggle in SwiftUI is to declare a Bool value in your app's state that will represent the current state of the toggle. This value is known as a binding and is used to bind the toggle to your app's state. You can then use the Toggle view and pass in the binding as a parameter to create the toggle.

Here's an example of how to create a toggle in SwiftUI:


  struct ContentView: View {
    @State private var isOn = false

    var body: some View {
        Toggle(isOn: $isOn) {
            Text("Toggle Me")
        }
    }
}

In this example, we create a ContentView struct that conforms to the View protocol. The ContentView has a @State property called isOn, a Bool value representing the current state of the toggle. We then use the Toggle view to create the toggle and bind it to the isOn property using the isOn: parameter.

In addition to the isOn: parameter, the Toggle view also has a label parameter that you can use to specify the text that you should display next to the toggle. In this example, we use the Text view to display the label "Toggle Me."

Customizing Your Toggle

You can customize the appearance of the toggle by using the foregroundColor, accentColor, and label modifiers. For example, to change the foreground color of the toggle to red and the accent color to green, you can do the following:


  Toggle(isOn: $isOn) {
    Text("Toggle Me")
}.foregroundColor(.blue)
.toggleStyle(SwitchToggleStyle(tint: .blue))

The foregroundColor modifier specifies the color of the toggle itself, while the toggleStyle modifier sets the color of the toggle when it is in the "on" state.

You can also change the style of the toggle completely to more of a button style where the button can be either on or off. To do this, use the toggleStyle modifier again and use the .button value.


  Toggle(isOn: $isOn) {
    Text("Toggle Me")
}.toggleStyle(.button)
.tint(.red)

Pretty cool, right?

Setting the Toggle Behavior

How about we add a bit more functionality to our toggle app? Let's create a simulation of a settings view where a user can choose to follow other users of a social network app.

We'll first need to create a separate struct that implements the identifiable protocol to create a list of toggles that the user can independently toggle. Then, we can track their states in a single collection.


  struct UserList: Identifiable {
    var id: String
    var isFollowing = false
}

Now create a @State variable that will contain a list of the structs previously defined. In this case, we're using a UserList containing an ID and a Bool indicating if you're following the user.


  @State var lists = [
  UserList(id: "Pewdipie", isFollowing: false),
  UserList(id: "Markiplier", isFollowing: false),
  UserList(id: "AJourneyForWisdom", isFollowing: false),
  UserList(id: "GameTheory", isFollowing: false),
  UserList(id: "QueenQuirks", isFollowing: false)]

Finally, create a Form container inside the body view and add the Toggle element inside. In this case, since you're dealing with a list, you can use a ForEach helper to represent them. Contain the toggle inside a Section container to keep the elements neatly aggregated.

You can also add a separate Toggle that would trigger all other toggles as a primary control.


  var body: some View {
    Form {
        Section {
            Text("Settings")
        }
        Section("Follow your favorite creators") {
            ForEach($lists) { $list in
                Toggle(list.id, isOn: $list.isFollowing)
                    .foregroundColor(.red)
                    .toggleStyle(SwitchToggleStyle(tint: .blue))
            }
        }
        Section("Options") {
            Toggle("Follow all", sources: $lists, isOn: \.isFollowing)
                .foregroundColor(.red)
                .toggleStyle(SwitchToggleStyle(tint: .blue))
        }
    }.padding()
}

Notice that there's no event handler taking care of the state of the toggles. If you run this code, all toggles respond accordingly. Why is this? Well, the isFollowing variable holds the state of the toggle in question. Since this is contained inside a @State variable, the state is synchronized between all views bound to it. So, when you tap on a toggle, the state of these variables changes.

toggle screenshot

Custom Event Management on SwiftUI Toggles

But what if you wanted to add some code to do something else when the user tapped the toggle? Well, you can use the onToggle modifier to specify a closure to execute when the toggle's state changes. This closure will be called every time the toggle's state changes. Swift will then pass a Bool value representing the new state of the toggle.

Here's an example of how to use the onToggle parameter:


  Toggle(list.id, isOn: $list.isFollowing)
  .foregroundColor(.red)
  .toggleStyle(SwitchToggleStyle(tint: .blue))
  .onChange(of: list.isFollowing) { state in
      print("toggled to \(state)")
  }

In this example, the closure will be called every time the toggle's state is changed. Additionally, the value parameter will be set to "true" if the toggle is turned on.

You can use this closure to perform any necessary actions in your app when the toggle's state is changed.

Before testing your work, you must do a little setup and ensure that all toggles respond accordingly

Testing Toggles in SwiftUI

Before testing your work, you must do a little setup and ensure that all toggles respond accordingly. First, add the accessibilityIdentifier modifier to the toggle elements and provide a unique name to them. In the case of the toggles inside the ForEach helper, you can use the current item ID.


  Form {
    Section {
        Text("Settings")
    }
    Section("Follow your favorite creators") {
        ForEach($lists) { $list in
            Toggle(list.id, isOn: $list.isFollowing)
                .foregroundColor(.red)
                .toggleStyle(SwitchToggleStyle(tint: .blue))
                .onChange(of: list.isFollowing) { state in
                    print("toggled to \(state)")
                }
                .accessibilityIdentifier(list.id)
        }
    }
    Section("Options") {
        Toggle("Follow all", sources: $lists, isOn: \.isFollowing)
            .foregroundColor(.red)
            .toggleStyle(SwitchToggleStyle(tint: .blue))
            .accessibilityIdentifier("FollowAll")
    }
}.padding()

Now, go to the UITest.swift class inside your testing target directory and add the following test case.


  func testTogglesRespondCorrectly() throws {
    // UI tests must launch the application that they test.
    let app = XCUIApplication()
    app.launch()
    // Get all toggles visible in the current view.
    let toggles = app.switches
    // Wait one second
    sleep(1)
    // Execute UI action
    toggles["AJourneyForWisdom"].tap()
    toggles["QueenQuirks"].tap()
    // Wait one second
    sleep(1)
    // Assert your assumptions
    XCTAssertEqual((toggles["AJourneyForWisdom"].value != nil), true, "Toggle is not responding!")
    XCTAssertEqual((toggles["QueenQuirks"].value != nil), true, "Toggle is not responding!")
    // Execute UI action
    toggles["FollowAll"].tap()
    // Wait one second
    sleep(1)
    // Assert your assumptions
    XCTAssertEqual((toggles["Pewdipie"].value != nil), true, "Toggle is not responding!")
    XCTAssertEqual((toggles["Markiplier"].value != nil), true, "Toggle is not responding!")
    XCTAssertEqual((toggles["AJourneyForWisdom"].value != nil), true, "Toggle is not responding!")
    XCTAssertEqual((toggles["GameTheory"].value != nil), true, "Toggle is not responding!")
    XCTAssertEqual((toggles["QueenQuirks"].value != nil), true, "Toggle is not responding!")
    // Assert your assumptions
    XCTAssertEqual((toggles["FollowAll"].value != nil), true, "Toggle is not responding!")
    // Wait one second
    sleep(1)
}

As you can see, you first retrieve all visible toggles in the app's current view. Then, you perform a UI action and validate your assumptions on the app's state. It's pretty straightforward.

Now, you can build from this example and validate more complex behaviors in your app.

If you don't want to deal with that hassle, I recommend checking out Waldo.io's comprehensive toolset for UI testing to avoid the complexities of developing a testing workflow. This toolset requires no coding and is easy to use, even for those who are not developers.

Moving Forward

Overall, toggles are a useful tool for allowing users to enable or disable features or options in your app. The Toggle view in SwiftUI makes it easy to add them to your app's UI.

You can find the complete project from which this article is based in this repository.

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.