2024 EssentialsSwiftUI & UI Frameworks
WWDC24 · 24 min · Essentials / SwiftUI & UI Frameworks
SwiftUI essentials
Join us on a tour of SwiftUI, Apple’s declarative user interface framework. Learn essential concepts for building apps in SwiftUI, like views, state variables, and layout. Discover the breadth of APIs for building fully featured experiences and crafting unique custom components. Whether you’re brand new to SwiftUI or an experienced developer, you’ll learn how to take advantage of what SwiftUI has to offer when building great apps.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 33 snippets
Declarative views
Text("Whiskers")
Image(systemName: "cat.fill")
Button("Give Treat") {
// Give Whiskers a treat
} Declarative views: layout
HStack {
Label("Whiskers", systemImage: "cat.fill")
Spacer()
Text("Tightrope walking")
} Declarative views: list
struct ContentView: View {
private var pets = Pet.samplePets
var body: some View {
List(pets) { pet in
HStack {
Label("Whiskers", systemImage: "cat.fill")
Spacer()
Text("Tightrope walking")
}
}
}
}
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
init(_ name: String, kind: Kind, trick: String) {
self.name = name
self.kind = kind
self.trick = trick
}
static let samplePets = [
Pet("Whiskers", kind: .cat, trick: "Tightrope walking"),
Pet("Roofus", kind: .dog, trick: "Home runs"),
Pet("Bubbles", kind: .fish, trick: "100m freestyle"),
Pet("Mango", kind: .bird, trick: "Basketball dunk"),
Pet("Ziggy", kind: .lizard, trick: "Parkour"),
Pet("Sheldon", kind: .turtle, trick: "Kickflip"),
Pet("Chirpy", kind: .bug, trick: "Canon in D")
]
} Declarative views: list
struct ContentView: View {
private var pets = Pet.samplePets
var body: some View {
List(pets) { pet in
HStack {
Label(pet.name, systemImage: pet.kind.systemImage)
Spacer()
Text(pet.trick)
}
}
}
}
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
init(_ name: String, kind: Kind, trick: String) {
self.name = name
self.kind = kind
self.trick = trick
}
static let samplePets = [
Pet("Whiskers", kind: .cat, trick: "Tightrope walking"),
Pet("Roofus", kind: .dog, trick: "Home runs"),
Pet("Bubbles", kind: .fish, trick: "100m freestyle"),
Pet("Mango", kind: .bird, trick: "Basketball dunk"),
Pet("Ziggy", kind: .lizard, trick: "Parkour"),
Pet("Sheldon", kind: .turtle, trick: "Kickflip"),
Pet("Chirpy", kind: .bug, trick: "Canon in D")
]
} Declarative and imperative programming
struct ContentView: View {
private var pets = Pet.samplePets
var body: some View {
Button("Add Pet") {
pets.append(Pet("Toby", kind: .dog, trick: "WWDC Presenter"))
}
List(pets) { pet in
HStack {
Label(pet.name, systemImage: pet.kind.systemImage)
Spacer()
Text(pet.trick)
}
}
}
}
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
init(_ name: String, kind: Kind, trick: String) {
self.name = name
self.kind = kind
self.trick = trick
}
static let samplePets = [
Pet("Whiskers", kind: .cat, trick: "Tightrope walking"),
Pet("Roofus", kind: .dog, trick: "Home runs"),
Pet("Bubbles", kind: .fish, trick: "100m freestyle"),
Pet("Mango", kind: .bird, trick: "Basketball dunk"),
Pet("Ziggy", kind: .lizard, trick: "Parkour"),
Pet("Sheldon", kind: .turtle, trick: "Kickflip"),
Pet("Chirpy", kind: .bug, trick: "Canon in D")
]
} Layout container
HStack {
Label("Whiskers", systemImage: "cat.fill")
Spacer()
Text("Tightrope walking")
} Container views
struct ContentView: View {
var body: some View {
HStack {
Image(whiskers.profileImage)
VStack(alignment: .leading) {
Label("Whiskers", systemImage: "cat.fill")
Text("Tightrope walking")
}
Spacer()
}
}
}
let whiskers = Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers")
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
var profileImage: String
init(_ name: String, kind: Kind, trick: String, profileImage: String) {
self.name = name
self.kind = kind
self.trick = trick
self.profileImage = profileImage
}
} View modifiers
struct ContentView: View {
var body: some View {
Image(whiskers.profileImage)
.clipShape(.circle)
.shadow(radius: 3)
.overlay {
Circle().stroke(.green, lineWidth: 2)
}
}
}
let whiskers = Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers")
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
var profileImage: String
init(_ name: String, kind: Kind, trick: String, profileImage: String) {
self.name = name
self.kind = kind
self.trick = trick
self.profileImage = profileImage
}
} Custom views: Intro
struct PetRowView: View {
var body: some View {
// ...
}
} Custom views
struct PetRowView: View {
var body: some View {
Image(whiskers.profileImage)
.clipShape(.circle)
.shadow(radius: 3)
.overlay {
Circle().stroke(.green, lineWidth: 2)
}
}
}
let whiskers = Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers")
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
var profileImage: String
init(_ name: String, kind: Kind, trick: String, profileImage: String) {
self.name = name
self.kind = kind
self.trick = trick
self.profileImage = profileImage
}
} Custom views: iteration
struct PetRowView: View {
var body: some View {
HStack {
Image(whiskers.profileImage)
.clipShape(.circle)
.shadow(radius: 3)
.overlay {
Circle()
.stroke(.green, lineWidth: 2)
}
Text("Whiskers")
Spacer()
}
}
}
let whiskers = Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers")
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
var profileImage: String
init(_ name: String, kind: Kind, trick: String, profileImage: String) {
self.name = name
self.kind = kind
self.trick = trick
self.profileImage = profileImage
}
} Custom views: view properties
struct PetRowView: View {
var body: some View {
HStack {
profileImage
Text("Whiskers")
Spacer()
}
}
private var profileImage: some View {
Image(whiskers.profileImage)
.clipShape(.circle)
.shadow(radius: 3)
.overlay {
Circle().stroke(.green, lineWidth: 2)
}
}
}
let whiskers = Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers")
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
var profileImage: String
init(_ name: String, kind: Kind, trick: String, profileImage: String) {
self.name = name
self.kind = kind
self.trick = trick
self.profileImage = profileImage
}
} Custom views: complete row view
struct PetRowView: View {
var body: some View {
HStack {
profileImage
VStack(alignment: .leading) {
Text("Whiskers")
Text("Tightrope walking")
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
}
}
private var profileImage: some View {
Image(whiskers.profileImage)
.clipShape(.circle)
.shadow(radius: 3)
.overlay {
Circle().stroke(.green, lineWidth: 2)
}
}
}
let whiskers = Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers")
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
var profileImage: String
init(_ name: String, kind: Kind, trick: String, profileImage: String) {
self.name = name
self.kind = kind
self.trick = trick
self.profileImage = profileImage
}
} Custom views: input properties
struct PetRowView: View {
var pet: Pet
var body: some View {
HStack {
profileImage
VStack(alignment: .leading) {
Text(pet.name)
Text(pet.trick)
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
}
}
private var profileImage: some View {
Image(pet.profileImage)
.clipShape(.circle)
.shadow(radius: 3)
.overlay {
Circle().stroke(pet.favoriteColor, lineWidth: 2)
}
}
}
struct Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
let id = UUID()
var name: String
var kind: Kind
var trick: String
var profileImage: String
var favoriteColor: Color
init(_ name: String, kind: Kind, trick: String, profileImage: String, favoriteColor: Color) {
self.name = name
self.kind = kind
self.trick = trick
self.profileImage = profileImage
self.favoriteColor = favoriteColor
}
} Custom views: reuse
PetRowView(pet: model.pet(named: "Whiskers"))
PetRowView(pet: model.pet(named: "Roofus"))
PetRowView(pet: model.pet(named: "Bubbles")) List composition
struct ContentView: View {
var model: PetStore
var body: some View {
List(model.allPets) { pet in
PetRowView(pet: pet)
}
}
}
class PetStore {
var allPets: [Pet] = [
Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers", favoriteColor: .green),
Pet("Roofus", kind: .dog, trick: "Home runs", profileImage: "Roofus", favoriteColor: .blue),
Pet("Bubbles", kind: .fish, trick: "100m freestyle", profileImage: "Bubbles", favoriteColor: .orange),
Pet("Mango", kind: .bird, trick: "Basketball dunk", profileImage: "Mango", favoriteColor: .green),
Pet("Ziggy", kind: .lizard, trick: "Parkour", profileImage: "Ziggy", favoriteColor: .purple),
Pet("Sheldon", kind: .turtle, trick: "Kickflip", profileImage: "Sheldon", favoriteColor: .brown),
Pet("Chirpy", kind: .bug, trick: "Canon in D", profileImage: "Chirpy", favoriteColor: .orange)
]
} List composition: ForEach
struct ContentView: View {
var model: PetStore
var body: some View {
List {
ForEach(model.allPets) { pet in
PetRowView(pet: pet)
}
}
}
}
class PetStore {
var allPets: [Pet] = [
Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers", favoriteColor: .green),
Pet("Roofus", kind: .dog, trick: "Home runs", profileImage: "Roofus", favoriteColor: .blue),
Pet("Bubbles", kind: .fish, trick: "100m freestyle", profileImage: "Bubbles", favoriteColor: .orange),
Pet("Mango", kind: .bird, trick: "Basketball dunk", profileImage: "Mango", favoriteColor: .green),
Pet("Ziggy", kind: .lizard, trick: "Parkour", profileImage: "Ziggy", favoriteColor: .purple),
Pet("Sheldon", kind: .turtle, trick: "Kickflip", profileImage: "Sheldon", favoriteColor: .brown),
Pet("Chirpy", kind: .bug, trick: "Canon in D", profileImage: "Chirpy", favoriteColor: .orange)
]
} List composition: sections
struct ContentView: View {
var model: PetStore
var body: some View {
List {
Section("My Pets") {
ForEach(model.myPets) { pet in
PetRowView(pet: pet)
}
}
Section("Other Pets") {
ForEach(model.otherPets) { pet in
PetRowView(pet: pet)
}
}
}
}
}
class PetStore {
var myPets: [Pet] = [
Pet("Roofus", kind: .dog, trick: "Home runs", profileImage: "Roofus", favoriteColor: .blue),
Pet("Sheldon", kind: .turtle, trick: "Kickflip", profileImage: "Sheldon", favoriteColor: .brown),
]
var otherPets: [Pet] = [
Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers", favoriteColor: .green),
Pet("Bubbles", kind: .fish, trick: "100m freestyle", profileImage: "Bubbles", favoriteColor: .orange),
Pet("Mango", kind: .bird, trick: "Basketball dunk", profileImage: "Mango", favoriteColor: .green),
Pet("Ziggy", kind: .lizard, trick: "Parkour", profileImage: "Ziggy", favoriteColor: .purple),
Pet("Chirpy", kind: .bug, trick: "Canon in D", profileImage: "Chirpy", favoriteColor: .orange)
]
} List composition: section actions
PetRowView(pet: pet)
.swipeActions(edge: .leading) {
Button("Award", systemImage: "trophy") {
// Give pet award
}
.tint(.orange)
ShareLink(item: pet, preview: SharePreview("Pet", image: Image(pet.name)))
} View updates
struct ContentView: View {
var model: PetStore
var body: some View {
List {
Section("My Pets") {
ForEach(model.myPets) { pet in
row(pet: pet)
}
}
Section("Other Pets") {
ForEach(model.otherPets) { pet in
row(pet: pet)
}
}
}
}
private func row(pet: Pet) -> some View {
PetRowView(pet: pet)
.swipeActions(edge: .leading) {
Button("Award", systemImage: "trophy") {
pet.giveAward()
}
.tint(.orange)
ShareLink(item: pet, preview: SharePreview("Pet", image: Image(pet.name)))
}
}
}
struct PetRowView: View {
var pet: Pet
var body: some View {
HStack {
profileImage
VStack(alignment: .leading) {
HStack(alignment: .firstTextBaseline) {
Text(pet.name)
if pet.hasAward {
Image(systemName: "trophy.fill")
.foregroundStyle(.orange)
}
}
Text(pet.trick)
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
}
}
private var profileImage: some View {
Image(pet.profileImage)
.clipShape(.circle)
.shadow(radius: 3)
.overlay {
Circle().stroke(pet.favoriteColor, lineWidth: 2)
}
}
}
class PetStore {
var myPets: [Pet] = [
Pet("Roofus", kind: .dog, trick: "Home runs", profileImage: "Roofus", favoriteColor: .blue),
Pet("Sheldon", kind: .turtle, trick: "Kickflip", profileImage: "Sheldon", favoriteColor: .brown),
]
var otherPets: [Pet] = [
Pet("Whiskers", kind: .cat, trick: "Tightrope walking", profileImage: "Whiskers", favoriteColor: .green),
Pet("Bubbles", kind: .fish, trick: "100m freestyle", profileImage: "Bubbles", favoriteColor: .orange),
Pet("Mango", kind: .bird, trick: "Basketball dunk", profileImage: "Mango", favoriteColor: .green),
Pet("Ziggy", kind: .lizard, trick: "Parkour", profileImage: "Ziggy", favoriteColor: .purple),
Pet("Chirpy", kind: .bug, trick: "Canon in D", profileImage: "Chirpy", favoriteColor: .orange)
]
}
class Pet: Identifiable {
enum Kind {
case cat
case dog
case fish
case bird
case lizard
case turtle
case rabbit
case bug
var systemImage: String {
switch self {
case .cat: return "cat.fill"
case .dog: return "dog.fill"
case .fish: return "fish.fill"
case .bird: return "bird.fill"
case .lizard: return "lizard.fill"
case .turtle: return "tortoise.fill"
case .rabbit: return "rabbit.fill"
case .bug: return "ant.fill"
}
}
}
var name: String
var kind: Kind
var trick: String
var profileImage: String
var favoriteColor: Color
var hasAward: Bool = false
init(_ name: String, kind: Kind, trick: String, profileImage: String, favoriteColor: Color) {
self.name = name
self.kind = kind
self.trick = trick
self.profileImage = profileImage
self.favoriteColor = favoriteColor
}
func giveAward() {
hasAward = true
}
}
extension Pet: Transferable {
static var transferRepresentation: some TransferRepresentation {
ProxyRepresentation { $0.name }
}
} State changes
struct RatingView: View {
var rating: Int = 5
var body: some View {
HStack {
Button("Decrease", systemImage: "minus.circle") {
rating -= 1
}
.disabled(rating == 0)
.labelStyle(.iconOnly)
Text(rating, format: .number.precision(.integerLength(2)))
.font(.title.bold())
Button("Increase", systemImage: "plus.circle") {
rating += 1
}
.disabled(rating == 10)
.labelStyle(.iconOnly)
}
}
} State changes: animation
struct RatingView: View {
var rating: Int = 5
var body: some View {
HStack {
Button("Decrease", systemImage: "minus.circle") {
withAnimation {
rating -= 1
}
}
.disabled(rating == 0)
.labelStyle(.iconOnly)
Text(rating, format: .number.precision(.integerLength(2)))
.font(.title.bold())
Button("Increase", systemImage: "plus.circle") {
withAnimation {
rating += 1
}
}
.disabled(rating == 10)
.labelStyle(.iconOnly)
}
}
} State changes: text content transition
struct RatingView: View {
var rating: Int = 5
var body: some View {
HStack {
Button("Decrease", systemImage: "minus.circle") {
withAnimation {
rating -= 1
}
}
.disabled(rating == 0)
.labelStyle(.iconOnly)
Text(rating, format: .number.precision(.integerLength(2)))
.contentTransition(.numericText(value: Double(rating)))
.font(.title.bold())
Button("Increase", systemImage: "plus.circle") {
withAnimation {
rating += 1
}
}
.disabled(rating == 10)
.labelStyle(.iconOnly)
}
}
} State changes: multiple state
struct RatingContainerView: View {
private var rating: Int = 5
var body: some View {
Gauge(value: Double(rating), in: 0...10) {
Text("Rating")
}
RatingView()
}
}
struct RatingView: View {
var rating: Int = 5
var body: some View {
HStack {
Button("Decrease", systemImage: "minus.circle") {
withAnimation {
rating -= 1
}
}
.disabled(rating == 0)
.labelStyle(.iconOnly)
Text(rating, format: .number.precision(.integerLength(2)))
.contentTransition(.numericText(value: Double(rating)))
.font(.title.bold())
Button("Increase", systemImage: "plus.circle") {
withAnimation {
rating += 1
}
}
.disabled(rating == 10)
.labelStyle(.iconOnly)
}
}
} State changes: state and binding
struct RatingContainerView: View {
private var rating: Int = 5
var body: some View {
Gauge(value: Double(rating), in: 0...10) {
Text("Rating")
}
RatingView(rating: $rating)
}
}
struct RatingView: View {
var rating: Int
var body: some View {
HStack {
Button("Decrease", systemImage: "minus.circle") {
withAnimation {
rating -= 1
}
}
.disabled(rating == 0)
.labelStyle(.iconOnly)
Text(rating, format: .number.precision(.integerLength(2)))
.contentTransition(.numericText(value: Double(rating)))
.font(.title.bold())
Button("Increase", systemImage: "plus.circle") {
withAnimation {
rating += 1
}
}
.disabled(rating == 10)
.labelStyle(.iconOnly)
}
}
} Adaptive buttons
Button("Reward", systemImage: "trophy") {
// Give pet award
}
// .buttonStyle(.borderless)
// .buttonStyle(.bordered)
// .buttonStyle(.borderedProminent) Adaptive toggles
Toggle("Nocturnal Mode", systemImage: "moon", isOn: $pet.isNocturnal)
// .toggleStyle(.switch)
// .toggleStyle(.checkbox)
// .toggleStyle(.button) Searchable
struct PetListView: View {
var viewModel: PetStoreViewModel
var body: some View {
List {
Section("My Pets") {
ForEach(viewModel.myPets) { pet in
row(pet: pet)
}
}
Section("Other Pets") {
ForEach(viewModel.otherPets) { pet in
row(pet: pet)
}
}
}
.searchable(text: $viewModel.searchText)
}
private func row(pet: Pet) -> some View {
PetRowView(pet: pet)
.swipeActions(edge: .leading) {
Button("Reward", systemImage: "trophy") {
pet.giveAward()
}
.tint(.orange)
ShareLink(item: pet, preview: SharePreview("Pet", image: Image(pet.name)))
}
}
}
class PetStoreViewModel {
var petStore: PetStore
var searchText: String = ""
init(petStore: PetStore) {
self.petStore = petStore
}
var myPets: [Pet] {
// For illustration purposes only. The filtered pets should be cached.
petStore.myPets.filter { searchText.isEmpty || $0.name.contains(searchText) }
}
var otherPets: [Pet] {
// For illustration purposes only. The filtered pets should be cached.
petStore.otherPets.filter { searchText.isEmpty || $0.name.contains(searchText) }
}
} Searchable: customization
struct PetListView: View {
var viewModel: PetStoreViewModel
var body: some View {
List {
Section("My Pets") {
ForEach(viewModel.myPets) { pet in
row(pet: pet)
}
}
Section("Other Pets") {
ForEach(viewModel.otherPets) { pet in
row(pet: pet)
}
}
}
.searchable(text: $viewModel.searchText, editableTokens: $viewModel.searchTokens) { $token in
Label(token.kind.name, systemImage: token.kind.systemImage)
}
.searchScopes($viewModel.searchScope) {
Text("All Pets").tag(PetStoreViewModel.SearchScope.allPets)
Text("My Pets").tag(PetStoreViewModel.SearchScope.myPets)
Text("Other Pets").tag(PetStoreViewModel.SearchScope.otherPets)
}
.searchSuggestions {
PetSearchSuggestions(viewModel: viewModel)
}
}
private func row(pet: Pet) -> some View {
PetRowView(pet: pet)
.swipeActions(edge: .leading) {
Button("Reward", systemImage: "trophy") {
pet.giveAward()
}
.tint(.orange)
ShareLink(item: pet, preview: SharePreview("Pet", image: Image(pet.name)))
}
}
} App definition
@main
struct SwiftUIEssentialsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
} App definition: multiple scenes
@main
struct SwiftUIEssentialsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
WindowGroup("Training History", id: "history", for: TrainingHistory.ID.self) { $id in
TrainingHistoryView(historyID: id)
}
WindowGroup("Pet Detail", id: "detail", for: Pet.ID.self) { $id in
PetDetailView(petID: id)
}
}
} Widgets
struct ScoreboardWidget: Widget {
var body: some WidgetConfiguration {
// ...
}
}
struct ScoreboardWidgetView: View {
var petTrick: PetTrick
var body: some View {
ScoreCard(rating: petTrick.rating)
.overlay(alignment: .bottom) {
Text(petTrick.pet.name)
.padding()
}
.widgetURL(petTrick.pet.url)
}
} Digital Crown rotation
ScoreCardStack(rating: $rating)
.focusable()
#if os(watchOS)
.digitalCrownRotation($rating, from: 0, through: 10)
#endif Resources
Related sessions
-
36 min -
55 min