SwiftUI Introduction
Build interfaces declaratively with views, state, and data-driven updates using SwiftUI.
What is SwiftUI?
SwiftUI is Apple’s modern UI framework for building apps on iOS, iPadOS, macOS, watchOS, and tvOS.
It is declarative: you describe what the UI should look like, and SwiftUI updates it when your data changes.
Why SwiftUI?
- Declarative syntax and reactive updates with
@State, @Binding, and ObservableObject.
- Unified API across Apple platforms.
- Live previews and fast iteration in Xcode.
Building our First “App”
First, you’ll build a simple counter view (ContentView) shown by the app entry (App.swift).
Next, you’ll refactor the same UI but move its state into a view model (ObservableObject), which scales better in real apps.
Step 1: Basic Example
Syntax: Use @State for local state and bind it to controls (e.g., Button); render with VStack/Text.
Example
ContentView.swift
App.swift
import SwiftUI
struct ContentView: View {
@State private var count = 0
var body: some View {
VStack(spacing: 12) {
Text("Count: \(count)")
Button("Increment") { count += 1 }
}
.padding()
}
}
This example shows a simple counter using @State and a button inside a VStack.
About ContentView.swift: In SwiftUI, views live in ordinary .swift files.
The starter view is typically named ContentView.swift, and the app’s entry point (App.swift) shows it in a WindowGroup.
Step 2: State with ObservableObject
Use ObservableObject to hold shared state, and @StateObject in the creating view to own its lifecycle.
Views update automatically when @Published properties change.
Syntax: class VM: ObservableObject { @Published var count }, @StateObject private var vm = VM(), bind UI to vm.count.
This example refactors the basic counter above.
The UI and behavior are the same (a counter label and button), but the state now lives in a CounterModel view model managed by @StateObject. This pattern scales better as your app grows.
Example
Demo.swift
ContentView.swift
App.swift
import SwiftUI
import Combine
class CounterModel: ObservableObject {
@Published var count = 0
func increment() { count += 1 }
}
This example promotes state into a ViewModel and updates the UI via @Published changes observed by the view.
Platform look: SwiftUI adapts controls to the platform. On iOS the button appears as blue text; on macOS it’s a bordered push button. The behavior is the same.
See iOS style in Xcode: run an iOS App target (iPhone simulator). If you’re in a macOS App and want a text-style button, add .buttonStyle(.plain) to the button.
Run Examples in Xcode
Standard workflow (used in this tutorial)
Every example is organized into three files so you can run it as a small app:
- Demo.swift: The example’s main code (view and/or supporting types).
- ContentView.swift: Shows the demo (references types from
Demo.swift).
- App.swift: Stable app entry with
WindowGroup { ContentView() }.
Tip: In your own Xcode project, keep a single ContentView. If you already have one from a previous example, reuse that file and update its body for the new demo. Alternatively, create a chooser menu and navigate to uniquely named demo views.
Optional: Canvas Preview
You can also run any example via Xcode’s Canvas Preview without changing your app entry point.
- Create a View file: In Xcode choose File > New > File… > SwiftUI View. Name it (for example,
FrameDemo.swift) and paste the example view’s code into it.
- Add a Preview: Ensure the file has a preview. Use either a
PreviewProvider or the #Preview macro.
PreviewProvider.swift
#Preview.swift
import SwiftUI
struct FrameDemo_Previews: PreviewProvider {
static var previews: some View { FrameDemo() }
}
- Open the Canvas: With the file selected, choose Editor > Canvas (or click the Canvas button). If you don’t see the preview, build the project once.
- Run the Preview: Click Resume / Play in the Canvas. Edits to the code will refresh the preview. Use the device picker to switch iPhone/iPad models.
- Interact: Previews are interactive—tap buttons, type into fields, etc.