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

2025 SwiftUI & UI FrameworksSwift

WWDC25 · 25 min · SwiftUI & UI Frameworks / Swift

Explore concurrency in SwiftUI

Discover how SwiftUI leverages Swift concurrency to build safe and responsive apps. Explore how SwiftUI uses the main actor by default and offloads work to other actors. Learn how to interpret concurrency annotations and manage async tasks with SwiftUI’s event loop for smooth animations and UI updates. You’ll leave knowing how to avoid data races and write code fearlessly.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 8 snippets

UI for extracting colors swift · at 2:45 ↗
// UI for extracting colors

struct ColorScheme: Identifiable, Hashable {
    var id = UUID()
    let imageName: String
    var colors: [Color]
}

@Observable
final class ColorExtractor {
    var imageName: String
    var scheme: ColorScheme?
    var isExtracting: Bool = false
    var colorCount: Float = 5

    func extractColorScheme() async {}
}

struct ColorExtractorView: View {
    @State private var model = ColorExtractor()

    var body: some View {
            ImageView(
                imageName: model.imageName,
                isLoading: model.isExtracting
            )
            EqualWidthVStack {
                ColorSchemeView(
                    isLoading: model.isExtracting,
                    colorScheme: model.scheme,
                    extractCount: Int(model.colorCount)
                )
                .onTapGesture {
                    guard !model.isExtracting else { return }
                    withAnimation { model.isExtracting = true }
                    Task {
                        await model.extractColorScheme()
                        withAnimation { model.isExtracting = false }
                    }
                }
                Slider(value: $model.colorCount, in: 3...10, step: 1)
                    .disabled(model.isExtracting)
            }
        }
    }
}
AppKit and UIKit require @MainActor: an example swift · at 5:55 ↗
// AppKit and UIKit require @MainActor
// Example: UIViewRepresentable

struct FancyUILabel: UIViewRepresentable {
    func makeUIView(context: Context) -> UILabel {
        let label = UILabel()
        // customize the label...
        return label
    }
}
UI for extracting colors swift · at 6:42 ↗
// UI for extracting colors

struct ColorScheme: Identifiable, Hashable {
    var id = UUID()
    let imageName: String
    var colors: [Color]
}

@Observable
final class ColorExtractor {
    var imageName: String
    var scheme: ColorScheme?
    var isExtracting: Bool = false
    var colorCount: Float = 5

    func extractColorScheme() async {}
}

struct ColorExtractorView: View {
    @State private var model = ColorExtractorModel()

    var body: some View {
            ImageView(
                imageName: model.imageName,
                isLoading: model.isExtracting
            )
            EqualWidthVStack(spacing: 30) {
                ColorSchemeView(
                    isLoading: model.isExtracting,
                    colorScheme: model.scheme,
                    extractCount: Int(model.colorCount)
                )
                .onTapGesture {
                    guard !model.isExtracting else { return }
                    withAnimation { model.isExtracting = true }
                    Task {
                        await model.extractColorScheme()
                        withAnimation { model.isExtracting = false }
                    }
                }
                Slider(value: $model.colorCount, in: 3...10, step: 1)
                    .disabled(model.isExtracting)
            }
        }
    }
}
Animated circle, part of color scheme view swift · at 8:26 ↗
// Part of color scheme view

struct SchemeContentView: View {
    let isLoading: Bool
    @State private var pulse: Bool = false

    var body: some View {
        ZStack {
            // Color wheel …

            Circle()
                .scaleEffect(isLoading ? 1.5 : 1)

            VStack {
                Text(isLoading ? "Please wait" : "Extract")

                if !isLoading {
                    Text("^[\(extractCount) color](inflect: true)")
                }
            }
            .visualEffect { [pulse] content, _ in
                content
                    .blur(radius: pulse ? 2 : 0)
            }
            .onChange(of: isLoading) { _, newValue in
                withAnimation(newValue ? kPulseAnimation : nil) {
                    pulse = newValue
                }
            }
        }
    }
}
UI for extracting colors swift · at 13:10 ↗
// UI for extracting colors

struct ColorExtractorView: View {
    @State private var model = ColorExtractor()

    var body: some View {
            ImageView(
                imageName: model.imageName,
                isLoading: model.isExtracting
            )
            EqualWidthVStack {
                ColorSchemeView(
                    isLoading: model.isExtracting,
                    colorScheme: model.scheme,
                    extractCount: Int(model.colorCount)
                )
                .onTapGesture {
                    guard !model.isExtracting else { return }
                    withAnimation { model.isExtracting = true }
                    Task {
                        await model.extractColorScheme()
                        withAnimation { model.isExtracting = false }
                    }
                }
                Slider(value: $model.colorCount, in: 3...10, step: 1)
                    .disabled(model.isExtracting)
            }
        }
    }
}
Part of color scheme view swift · at 13:47 ↗
// Part of color scheme view

struct SchemeContentView: View {
    let isLoading: Bool
    @State private var pulse: Bool = false

    var body: some View {
        ZStack {
            // Color wheel …

            Circle()
                .scaleEffect(isLoading ? 1.5 : 1)

            VStack {
                Text(isLoading ? "Please wait" : "Extract")

                if !isLoading {
                    Text("^[\(extractCount) color](inflect: true)")
                }
            }
            .visualEffect { [pulse] content, _ in
                content
                    .blur(radius: pulse ? 2 : 0)
            }
            .onChange(of: isLoading) { _, newValue in
                withAnimation(newValue ? kPulseAnimation : nil) {
                    pulse = newValue
                }
            }
        }
    }
}
UI for extracting colors swift · at 17:42 ↗
// UI for extracting colors

struct ColorExtractorView: View {
    @State private var model = ColorExtractor()

    var body: some View {
            ImageView(
                imageName: model.imageName,
                isLoading: model.isExtracting
            )
            EqualWidthVStack {
                ColorSchemeView(
                    isLoading: model.isExtracting,
                    colorScheme: model.scheme,
                    extractCount: Int(model.colorCount)
                )
                .onTapGesture {
                    guard !model.isExtracting else { return }
                    withAnimation { model.isExtracting = true }
                    Task {
                        await model.extractColorScheme()
                        withAnimation { model.isExtracting = false }
                    }
                }
                Slider(value: $model.colorCount, in: 3...10, step: 1)
                    .disabled(model.isExtracting)
            }
        }
    }
}
Animate colors as they appear by scrolling swift · at 18:55 ↗
// Animate colors as they appear by scrolling

struct SchemeHistoryItemView: View {
    let scheme: ColorScheme
    @State private var isShown: Bool = false

    var body: some View {
        HStack(spacing: 0) {
            ForEach(scheme.colors) { color in
                color
                    .offset(x: 0, y: isShown ? 0 : 60)
            }
        }
        .onScrollVisibilityChange(threshold: 0.9) {
            guard !isShown else { return }
            withAnimation {
                isShown = $0
            }
        }
    }
}

Resources