SwiftUIMiddleTechnical

В чём разница между модификаторами .onAppear и .task?

.onAppear — синхронный колбэк без поддержки async/await и без автоотмены; .task запускает async-замыкание и автоматически отменяет его, когда вью исчезает. Для сетевых запросов и AsyncSequence используйте .task.

Разница между .onAppear и .task

Оба модификатора реагируют на появление представления на экране, но имеют принципиально разную природу: .onAppear — синхронный колбэк, .task — встроенный механизм запуска асинхронных задач с автоматической отменой.

.onAppear

  • Вызывается синхронно в главном потоке в момент появления вью.
  • Не поддерживает async/await напрямую — чтобы запустить асинхронный код, нужно явно создавать Task { ... }.
  • Задача не отменяется автоматически при исчезновении вью.
  • Подходит для синхронных побочных эффектов: аналитика, изменение состояния, запуск таймера.
struct ExampleView: View {
    @State private var items: [Item] = []

    var body: some View {
        List(items) { item in Text(item.name) }
            .onAppear {
                // Синхронно
                Analytics.log("screen_viewed", name: "ExampleView")

                // Асинхронный код требует явного Task
                Task {
                    items = try await ItemService.fetch()
                }
            }
    }
}

.task

  • Запускает async-замыкание при появлении вью и автоматически отменяет (Task.cancel()) его при исчезновении.
  • Принимает опциональный id: — при изменении id задача перезапускается, старая отменяется.
  • Приоритет задачи настраивается: .task(priority: .background) { ... }.
  • Подходит для сетевых запросов, подписок на AsyncSequence, любых асинхронных операций.
struct ExampleView: View {
    @State private var items: [Item] = []
    let categoryID: UUID

    var body: some View {
        List(items) { item in Text(item.name) }
            .task(id: categoryID) {
                // Запускается при появлении и при смене categoryID
                do {
                    items = try await ItemService.fetch(category: categoryID)
                } catch is CancellationError {
                    // Вью исчезло — задача отменена, это нормально
                } catch {
                    print("Ошибка: \(error)")
                }
            }
    }
}

Подписка на AsyncSequence

.task {
    for await notification in NotificationCenter.default
        .notifications(named: .NSManagedObjectContextDidSave)
        .map({ $0 }) {
        await refreshData()
    }
    // Цикл автоматически завершится при отмене задачи
}

Сравнительная таблица

  • async/await: .task — да, нативно; .onAppear — нет, нужен явный Task { }
  • Автоотмена при disappear: .task — да; .onAppear — нет
  • Перезапуск при смене id: .task(id:) — да; .onAppear — нет
  • Синхронные побочные эффекты: оба подходят

Подводные камни

  • Утечка задачи в .onAppear — если внутри .onAppear создан Task { } без хранения токена, задача продолжает выполняться после исчезновения вью и может обновлять уже не существующее состояние.
  • Игнорирование CancellationError в .task — не обрабатывать отмену как ошибку: повторные попытки (retry) при отмене создают неотменяемые циклы.
  • id: Equatable — часто меняющиеся значения — если id в .task(id:) меняется очень часто (например, текст поискового запроса без debounce), задача будет постоянно перезапускаться и отменяться, создавая лишнюю нагрузку.
  • Двойной вызов в preview — в SwiftUI Preview и при hot reload .onAppear/.task могут вызываться дважды; код должен быть идемпотентным.
  • .task не является заменой .onAppear везде — для чисто синхронных операций (установка фокуса, трекинг аналитики) использование .task избыточно и добавляет ненужный overhead планировщика.
  • Приоритет задачи — по умолчанию .task использует .userInitiated; для фоновых операций явно передавайте priority: .background, иначе тяжёлые вычисления блокируют UI.

Common mistakes

  • Сводить «модификаторами .onAppear и .task» к синтаксису и не объяснять идентичность view.
  • Игнорировать жизненный цикл, основной поток или момент освобождения ресурсов в сценарии swiftui-15.
  • Выбирать API по привычке, не проверяя состояние, ошибки, доступность и платформенные ограничения.

What the interviewer is testing

  • Формулирует точную модель для «модификаторами .onAppear и .task» и подтверждает ее корректным примером.
  • Умеет связать ответ с инвалидация body, тестированием и отладкой на устройстве.
  • Называет ограничения подхода swiftui-15, включая производительность, память и сопровождение.

Sources

Related topics

В чём разница между модификаторами `.onAppear` и `.task`? | Talanto