Swift: SwiftUI Fundamentals
SwiftUI is Apple's declarative UI framework (iOS 13+). Views are value types (structs) — the framework diffs and redraws efficiently. State changes drive UI updates automatically.
Views & Layout
import SwiftUI
// Every SwiftUI view is a struct conforming to View
struct ContentView: View {
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("Hello, World!")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.primary)
HStack {
Image(systemName: "star.fill")
.foregroundColor(.yellow)
Text("4.8 rating")
}
Spacer()
Button("Get Started") {
// action
}
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)
}
.padding()
.background(Color(.systemBackground))
}
}
// List view
struct UserListView: View {
let users: [User]
var body: some View {
List(users, id: \.id) { user in
NavigationLink(destination: UserDetailView(user: user)) {
Label(user.name, systemImage: "person.circle")
}
}
.navigationTitle("Users")
}
}State & Data Flow
// @State — local view state
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: \(count)")
Button("+") { count += 1 }
}
}
}
// @Binding — pass state to child view
struct ToggleView: View {
@Binding var isOn: Bool
var body: some View {
Toggle("Enable", isOn: $isOn)
}
}
// @StateObject — own an ObservableObject
// @ObservedObject — receive an ObservableObject from parent
// @EnvironmentObject — inject from ancestor
class CartStore: ObservableObject {
@Published var items: [CartItem] = []
func add(_ item: CartItem) {
items.append(item)
}
}
struct ShopView: View {
@StateObject private var cart = CartStore()
var body: some View {
NavigationStack {
ProductListView()
.environmentObject(cart)
}
}
}
struct ProductListView: View {
@EnvironmentObject var cart: CartStore
// ...
}Navigation & Sheets
// NavigationStack (iOS 16+ — replaces NavigationView)
struct AppView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: User.self) { user in
UserDetailView(user: user)
}
.navigationDestination(for: Post.self) { post in
PostDetailView(post: post)
}
}
}
}
// Sheet (modal)
struct MainView: View {
@State private var showingSheet = false
@State private var showingFullScreen = false
var body: some View {
Button("Show Sheet") { showingSheet = true }
.sheet(isPresented: $showingSheet) {
DetailSheet()
.presentationDetents([.medium, .large]) // iOS 16+
}
.fullScreenCover(isPresented: $showingFullScreen) {
FullView()
}
}
}
// Alert
.alert("Delete item?", isPresented: $showingAlert) {
Button("Delete", role: .destructive) { deleteItem() }
Button("Cancel", role: .cancel) { }
}Async Image, Task & ViewModifiers
// AsyncImage (built-in async image loading)
AsyncImage(url: URL(string: user.avatarURL)) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 60, height: 60)
.clipShape(Circle())
} placeholder: {
ProgressView()
.frame(width: 60, height: 60)
}
// Task — run async work tied to view lifecycle
struct PostsView: View {
@State private var posts: [Post] = []
@State private var isLoading = false
var body: some View {
List(posts, id: \.id) { Text($0.title) }
.task {
isLoading = true
posts = await PostService.fetchAll()
isLoading = false
}
}
}
// Custom ViewModifier
struct CardStyle: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(12)
.shadow(radius: 2)
}
}
extension View {
func cardStyle() -> some View {
modifier(CardStyle())
}
}
// Usage
Text("Hello").cardStyle()Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free