Skip to content

How to use if-else and conditional statements in SwiftUI | SwiftUI Bootcamp #23

Every real app shows different UI based on what's happening — a loading spinner while data loads, an error state when a request fails, an empty state when a list has no items. This lesson teaches you how to use if, else if, and else inside SwiftUI's view builder to make your UI respond to state.

What You'll Learn

  • How to use if / else directly inside a SwiftUI body to conditionally include or exclude views
  • How a Bool flag like isLoading drives UI changes through the view builder
  • What happens to a view's identity when it is inserted or removed with a conditional (versus just made invisible)

Mental Model

Think of your view's body as a stage play script. Each if block is a stage direction: "if the actor is available, they walk on stage here — otherwise, leave the stage empty." SwiftUI reads the script top to bottom. When a condition changes, SwiftUI re-reads the script, and views that no longer satisfy their condition are literally removed from the view hierarchy — not just hidden.

This is fundamentally different from setting .opacity(0) or .hidden(). Those keep the view in the hierarchy (taking up space or triggering lifecycle events). An if statement that evaluates to false means the view was never added at all.

Detailed Explanation

SwiftUI's @ViewBuilder function builder, which powers body and most container views, understands if, if-else, and switch statements. Each branch must produce either a view or nothing. This is why you can write if isLoading { ProgressView() } without an else — the "no view" case is valid.

When a condition transitions from false to true, SwiftUI inserts the new view and runs any associated .onAppear modifiers. When it transitions from true to false, SwiftUI removes the view and runs .onDisappear. This insertion/removal behavior is important to understand because it resets any local @State inside the conditionally shown view — the view is brand new each time the condition becomes true.

Conditionals in the view builder are composable. You can use && and || to combine multiple Bool values in one condition: if showCircle || showRectangle { ... }. The commented-out code in this lesson demonstrates exactly that pattern — a combined shape appearing whenever either of two flags is on.

Use if-else when you want to swap one view for another (like swapping ProgressView() for real content). Use if without else when you want to optionally add a view to a layout that is otherwise complete.

Code Structure

ConditionalBootcamp.swift demonstrates a live isLoading flag that toggles a ProgressView. The file also contains commented-out code showing how to extend the pattern to multiple independent flags and combined conditions — uncomment and experiment with these sections to deepen your understanding.

Complete Code

ConditionalBootcamp.swift

swift
import SwiftUI

struct ConditionalBootcamp: View {
    
    @State var showCircle: Bool = false      // controls whether the circle is in the view hierarchy
    @State var showRectangle: Bool = false
    @State var isLoading: Bool = false       // the active flag in this demo
    var body: some View {
        VStack(spacing: 20) {
            
            Button("IS LOADING: \(isLoading.description)") { // description converts Bool to "true"/"false" string
                isLoading.toggle() // flips the Bool; SwiftUI re-evaluates body
            }
            
            if isLoading {
                ProgressView() // inserted when isLoading becomes true; removed when it becomes false
            } else {
                // empty else branch — nothing renders here when not loading
            }
            
//            Button("Circle Button: \(showCircle.description)") {
//                showCircle.toggle()
//            }
//            Button("Rectangle Button: \(showRectangle.description)") {
//                showRectangle.toggle()
//            }
//
//            if showCircle {
//                Circle()
//                    .frame(width: 100, height: 100)
//            }
//
//            if showRectangle {
//                Rectangle()
//                    .frame(width: 100, height: 100)
//            }
//
//            if showCircle || showRectangle { // combined condition — appears when either flag is true
//                RoundedRectangle(cornerRadius: 25.0)
//                    .frame(width: 200, height: 100)
//            }
            
            Spacer()
        }
    }
}

struct ConditionalBootcamp_Previews: PreviewProvider {
    static var previews: some View {
        ConditionalBootcamp()
    }
}

Code Walkthrough

  1. @State var isLoading: Bool = false — A Boolean flag that represents a distinct UI state. This is the minimal state needed: one bit of information drives the entire UI difference between "loading" and "idle".

  2. Button("IS LOADING: \(isLoading.description)") — The button label reflects the current state by embedding isLoading.description in the title. This is a useful debugging technique — the button tells you what it will toggle from, not just what it does.

  3. isLoading.toggle() — Swift's built-in toggle() method on Bool. Equivalent to isLoading = !isLoading but more readable and less error-prone.

  4. if isLoading { ProgressView() } — The @ViewBuilder processes this conditional and either includes ProgressView in the layout or excludes it. There is no hidden view — the slot is genuinely empty when isLoading is false.

  5. else { } empty branch — An explicit empty else is valid Swift. In practice you'd fill it with the content to show when not loading, or drop the else entirely if you only want something to appear conditionally.

  6. Commented-out combined conditions — The if showCircle || showRectangle pattern shows how conditionals in SwiftUI mirror regular Swift logic. Uncomment these blocks and try toggling each button to see how independent flags can drive a shared visual element.

Common Mistakes

Mistake: Using .opacity(0) or .hidden() when you actually want to remove a view
Views hidden with .opacity(0) still exist in the hierarchy, still receive taps (unless you also set .allowsHitTesting(false)), and still trigger .onAppear. Use an if statement when you genuinely want the view absent.

Mistake: Expecting @State inside a conditionally shown view to persist across toggles
Each time a view is inserted (condition becomes true), its @State is initialized fresh. If you need state to persist when the view is toggled off and back on, lift that state to the parent.

Mistake: Writing complex business logic inside a condition in body
Avoid if dataService.isAuthenticated && user.age > 18 && !flags.maintenanceMode. Extract this into a computed Bool property on your view or view model: var canShowContent: Bool { ... }. This keeps body readable and makes the condition testable.

Key Takeaways

  • if in a SwiftUI view builder inserts and removes views from the hierarchy — it does not just toggle visibility.
  • Bool.toggle() is the idiomatic way to flip a Boolean flag in a button action.
  • Use clear, named state variables (isLoading, showCircle) so that any conditional in body reads like plain English.

Last updated: June 27, 2026

Released under the MIT License.