iOS with SwiftUI · SwiftUI Foundations: Views, Layout, Lists and State

Your First iPhone App with SwiftUI

Create a project in Xcode, understand the App and View structs, preview your UI live with #Preview, and run the app in the simulator or on your iPhone.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A "Hello" screen with a button that counts taps, running in the iOS Simulator and on your own iPhone. You'll create the project, read the code Xcode generates, use live previews and make your first change.

The idea

SwiftUI is Apple's declarative UI framework for iPhone, iPad, Mac, Apple Watch, Apple TV and Vision Pro. You describe what the UI should look like for the current state, and SwiftUI works out how to update the screen when that state changes.

The pieces of a new project:

Piece What it does
@main struct MyApp: App the entry point; declares the app's scenes (windows)
struct ContentView: View a view: a struct whose body returns other views
@State state owned by a view; changing it re-renders the view
#Preview renders the view live in Xcode's canvas as you type
Modifiers (.font, .padding) return a new, modified view

Views are lightweight structs, recreated often; SwiftUI keeps their state separately, so recreating a view doesn't lose @State.

The code

In Xcode choose File > New > Project > iOS > App, with SwiftUI as the interface:

import SwiftUI

@main
struct HelloApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State private var taps = 0

    var body: some View {
        VStack(spacing: 16) {
            Text("Hello from my iPhone")
                .font(.title2.bold())
            Button(tapLabel(taps)) {
                taps += 1
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
    }
}

func tapLabel(_ taps: Int) -> String {
    "Tapped \(taps) \(taps == 1 ? "time" : "times")"
}

#Preview {
    ContentView()
}

Press Cmd-R to run in the simulator. To run on your iPhone, connect it, turn on Developer Mode (Settings > Privacy & Security), choose it as the run destination and sign with your Apple ID under Signing & Capabilities.

Try the logic

Views should stay thin: the label is a plain function, so it runs and tests without a device:

The counter's label logic
func tapLabel(_ taps: Int) -> String {
    "Tapped \(taps) \(taps == 1 ? "time" : "times")"
}

var taps = 0
print(tapLabel(taps))
taps += 1
print(tapLabel(taps))
taps += 2
print(tapLabel(taps))

Output

Tapped 0 times
Tapped 1 time
Tapped 3 times

Run this on your computer — see “Get Started” for setup.

Common mistakes

  • Plain properties for changing values. var taps = 0 in a view can't change (views are immutable structs); use @State private var.
  • Making @State public or passing it in. @State is owned by the view that declares it; pass values down and use @Binding for two-way access.
  • Heavy work in body. body runs often; never start network calls or sort large arrays there.
  • Ignoring previews. Previews with sample data catch layout problems in dark mode and large text sizes without running the app.

Key points

  • A SwiftUI app is an App struct with scenes; screens are View structs with a body.
  • @State holds a view's own changing values; changing it re-renders the view.
  • Modifiers return new views and are applied in order.
  • Use #Preview for fast iteration and keep logic in plain, testable functions.

Further reading: SwiftUI tutorials, SwiftUI documentation.

Practice questions

Why are SwiftUI views structs, and where does their state live?

Answer

Views are lightweight value types that describe the UI and are recreated whenever SwiftUI needs a new description. State declared with @State is stored by SwiftUI outside the struct, tied to the view's identity in the hierarchy, so it survives the struct being recreated.

What does the some keyword mean in var body: some View?

Answer

It's an opaque return type: body returns one specific concrete View type that the compiler knows, but callers only see that it conforms to View. This lets SwiftUI keep full type information for performance without you writing the long generic type.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?