Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2023 SwiftUI & UI Frameworks

WWDC23 · 22 min · SwiftUI & UI Frameworks

Demystify SwiftUI performance

Learn how you can build a mental model for performance in SwiftUI and write faster, more efficient code. We’ll share some of the common causes behind performance issues and help you triage hangs and hitches in SwiftUI to create more responsive views in your app.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

  • 0:00 — Introduction and performance feedback loop
  • 3:30 — Dependencies
  • 10:48 — Faster view updates
  • 13:24 — Identity in List and Table

Code shown on screen · 16 snippets

DogView swift · at 3:59 ↗
struct DogView: View {
  @Environment(\.isPlayTime) private var isPlayTime
  var dog: Dog
  var body: some View {
  	Text(dog.name)
  		.font(nameFont)
  	Text(dog.breed)
  		.font(breedFont)
  		.foregroundStyle(.secondary)
  		
  	ScalableDogImage(dog)
  	
  	DogDetailView(dog)
  	
  	LetsPlayButton()
  		.disabled(dog.isTired)
  }
 }
}
ScalableDogImage swift · at 4:00 ↗
struct ScalableDogImage: View {
	@State private var scaleToFill = false
	var dog: Dog
	
	var body: some View {
		dog.image
			.resizable()
			.aspectRatio(
				contentMode: scaleToFill ? .fill : .fit)
			.frame(maxHeight: scaleToFill ? 500 : nil)
			.padding(.vertical, 16)
			.onTapGesture {
				withAnimation { scaleToFill.toggle() }
			}
	}
}
printChanges bash · at 4:01 ↗
expression Self._printChanges()
ScalableDogImage + printChanges swift · at 4:02 ↗
struct ScalableDogImage: View {
	@State private var scaleToFill = false
	var dog: Dog
	
	var body: some View {
    let _ = Self._printChanges()
		dog.image
			.resizable()
			.aspectRatio(
				contentMode: scaleToFill ? .fill : .fit)
			.frame(maxHeight: scaleToFill ? 500 : nil)
			.padding(.vertical, 16)
			.onTapGesture {
				withAnimation { scaleToFill.toggle() }
			}
	}
}
ScaleableDogImage swift · at 8:46 ↗
struct ScalableDogImage: View {
	@State private var scaleToFill = false
	var dog: Dog
	
	var body: some View {
		dog.image
			.resizable()
			.aspectRatio(
				contentMode: scaleToFill ? .fill : .fit)
			.frame(maxHeight: scaleToFill ? 500 : nil)
			.padding(.vertical, 16)
			.onTapGesture {
				withAnimation { scaleToFill.toggle() }
			}
	}
}
Updated DogView swift · at 8:47 ↗
struct DogView: View {
  @Environment(\.isPlayTime) private var isPlayTime
  var dog: Dog
  var body: some View {
  	Text(dog.name)
  		.font(nameFont)
  	Text(dog.breed)
  		.font(breedFont)
  		.foregroundStyle(.secondary)
  		
  	ScalableDogImage(dog)
  	
  	DogDetailView(dog)
  	
  	LetsPlayButton()
  		.disabled(dog.isTired)
  }
 }
}
Final DogView swift · at 8:48 ↗
struct DogView: View {
  @Environment(\.isPlayTime) private var isPlayTime
  var dog: Dog
  var body: some View {
  	DogHeader(name: dog.name, breed: dog.breed)
  		
  	ScalableDogImage(dog.image)
  	
  	DogDetailView(dog)
  	
  	LetsPlayButton()
  		.disabled(dog.isTired)
  }
 }
}
DogRootView and FetchModel swift · at 12:22 ↗
struct DogRootView: View {
	@State private var model = FetchModel()
	
	var body: some View {
		DogList(model.dogs)
	}
}

@Observable class FetchModel {
	var dogs: [Dog]
	
	init() {
		fetchDogs()
	}
	
	func fetchDogs() {
		// Takes a long time
	}
}
Updated DogRootView and FetchModel swift · at 12:23 ↗
struct DogRootView: View {
	@State private var model = FetchModel()
	
	var body: some View {
		DogList(model.dogs)
			.task { await model.fetchDogs() }
	}
}

@Observable class FetchModel {
	var dogs: [Dog]
	
	init() {}
	
	func fetchDogs() async {
		// Takes a long time
	}
}
List swift · at 15:12 ↗
List {
	ForEach(dogs) {
		DogCell(dog: $0)
	}
}
List Again swift · at 16:08 ↗
List {
	ForEach(dogs) {
		DogCell(dog: $0)
	}
}
List Fixed swift · at 17:35 ↗
List {
	ForEach(tennisBallDogs) { dog in
		DogCell(dog)
	}
}
Sectioned List swift · at 18:25 ↗
// Sectioned example
struct DogsByToy: View {
	var model: DogModel
	var body: some View {
		List {
			ForEach(model.dogToys) { toy in
				Section(toy.name) {
					ForEach(model.dogs(toy: toy)) { dog in
						DogCell(dog)
					}
				}
			}
		}
	}
}
DogTable swift · at 19:21 ↗
struct DogTable: View {
	var dogs: [Dog]
	var body: some View {
		Table(of: Dog.self) {
			// Columns
		} rows: {
			ForEach(dogs) { dog in
				TableRow(dog)
			}
		}
	}
}
DogTable Brief swift · at 19:22 ↗
struct DogTable: View {
	var dogs: [Dog]
	var body: some View {
		Table(of: Dog.self) {
			// Columns
		} rows: {
			ForEach(dogs)
		}
	}
}
DogTable Different IDs swift · at 20:06 ↗
struct DogTable: View {
	var dogs: [Dog]
	var body: some View {
		Table(of: Dog.self) {
			// Columns
		} rows: {
			ForEach(dogs) { dog in
				TableRow(dog.bestFriend)
			}
		}
	}
}