.onAppear — модифікатор SwiftUI, який виконує замикання при додаванні View в ієрархію інтерфейсу. Виклик відбувається одноразово за появу екземпляра на екрані та слугує основною точкою для завантаження даних, запуску анімацій і надсилання аналітичних подій. За даними Apple Developer Documentation (2026), onAppear гарантує виконання до першого рендерингу, але не гарантує виклик при кожному повторному показі, якщо View залишається в пам'яті. Докладніше про SwiftUI читайте в матеріалі про SwiftUI.
Головне
.onAppear — модифікатор View у SwiftUI, який приймає замикання Void і виконує його в момент, коли View стає видимою на екрані. Цей модифікатор є частиною системи життєвого циклу SwiftUI-компонентів поряд з .onDisappear і .task. Apple представила onAppear разом з виходом SwiftUI в iOS 13 і watchOS 6 як заміну viewDidLoad з UIKit.
Синтаксично .onAppear модифікує будь-яку View і повертає ту ж View з прикріпленою дією. Компоновщик SwiftUI викликає передане замикання один раз, коли представлення додається в ієрархію та проходить стадію рендерингу. Якщо View видаляється і потім знову додається (наприклад, при скролі в списку), onAppear викликається повторно — ця поведінка часто стає джерелом неочікуваних багів.
Базовий синтаксис модифікатора мінімалістичний: onAppear без параметрів. У SwiftUI немає можливості передати пріоритет або анімацію — замикання виконується синхронно в основному потоці відразу після рендерингу.
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
.onAppear {
print("View appeared on screen")
}
}
}
Обмеження: onAppear не підтримує async/await безпосередньо. Для асинхронних операцій всередині замикання потрібен Task {} або окрема функція з async/await, що викликається через Task.detached. Це робить onAppear менш зручним для мережевих запитів порівняно з модифікатором .task.
.onAppear вбудовується в конвеєр рендерингу SwiftUI на етапі layout+render. Коли SwiftUI обчислює тіло View та виявляє зміну ієрархії, він запускає колбеки onAppear для всіх щойно доданих представлень. Порядок виклику відповідає порядку вкладеності: спочатку onAppear у батька, потім у дочірніх елементів.
Важлива особливість SwiftUI — onAppear не прив'язаний до появи на фізичному екрані. Модифікатор викликається, коли View додається в ієрархію незалежно від того, чи видна вона користувачеві (наприклад, за межами екрана в ScrollView). Це відрізняє SwiftUI від UIKit, де viewWillAppear спрацьовує лише при реальній появі.
Порядок виклику підпорядковується правилу parent-first: VStack або NavigationView спочатку отримує onAppear, потім кожен дочірній елемент по порядку. Це критично для ініціалізації shared-ресурсів: якщо дочірні елементи залежать від даних, що завантажуються батьком, вони повинні перевіряти доступність через Optional.
struct ParentView: View {
var body: some View {
VStack {
ChildView()
ChildView()
}
.onAppear {
print("Parent onAppear — first")
}
}
}
struct ChildView: View {
var body: some View {
Text("Child")
.onAppear {
print("Child onAppear")
}
}
}
Вивід у консоль буде: Parent onAppear — перший, потім двічі Child onAppear у порядку розташування. Ця поведінка гарантована Apple і стабільна у всіх версіях SwiftUI (iOS 13–18).
.onAppear має кілька сценаріїв виклику, які залежать від контейнера та навігації. У NavigationStack onAppear спрацьовує при кожному push нового контролера та при pop — для кореневого контролера. У TabView перемикання вкладок викликає onAppear для вкладки, яка відображається, та onDisappear для прихованої.
У List та ScrollView onAppear викликається для комірок, які потрапили в область видимості або знаходяться в буфері попереднього рендерингу. iOS 18 ввела prefetch-механізм, який може викликати onAppear для комірок за 2–3 екрани до скролу — це прискорює сприйняття, але може провокувати зайві мережеві запити.
NavigationStack (iOS 16+) керує стеком екранів інакше, ніж NavigationView. При push нового екрана onAppear спрацьовує лише у нового екрана, а поточний не отримує onDisappear до реального видалення. При pop відбувається зворотний процес: onDisappear у покинутого екрана, onAppear у того, що повертається.
| Сценарій | onAppear | onDisappear |
|---|---|---|
| Push | Новий екран | Ні (екран залишається в стеку) |
| Pop | Екран, що повертається | Покинутий екран |
| Перемикання вкладки | Нова вкладка | Стара вкладка |
| Закриття sheet | Батьківський екран | Відкритий sheet |
Практичне застосування onAppear охоплює три основні категорії: завантаження даних, запуск анімацій та надсилання аналітики. Кожен сценарій потребує врахування особливостей життєвого циклу SwiftUI, щоб уникнути дублювальних викликів та витоків пам'яті.
Завантаження даних — найчастіший сценарій onAppear. Всередині замикання створюється Task для async-виклику, а результат зберігається у @State або @StateObject. Важно перевіряти, чи не завантажені дані повторно, використовуючи прапорець isLoading або перевірку на nil.
struct ProfileView: View {
@StateObject private var viewModel = ProfileViewModel()
var body: some View {
VStack {
if viewModel.isLoading {
ProgressView()
} else {
Text(viewModel.userName)
}
}
.onAppear {
guard viewModel.userName == nil else { return }
Task {
await viewModel.loadProfile()
}
}
}
}
Захист від повторного отримання — критична практика. Якщо SwiftUI перестворить View (наприклад, при повороті екрана), onAppear викличеться знову без захисту. Альтернатива — модифікатор .task, який автоматично скасовує попередній запит.
Анімація входу використовує onAppear для зміни state-змінних, які тригерять анімацію через withAnimation або animation-модифікатор. Типовий патерн: початковий стан (opacity 0, offset 100), перехід у фінальний (opacity 1, offset 0) при появі.
struct AnimatedCard: View {
@State private var isVisible = false
var body: some View {
RoundedRectangle(cornerRadius: 12)
.fill(Color.blue)
.opacity(isVisible ? 1 : 0)
.offset(y: isVisible ? 0 : 50)
.animation(.spring(), value: isVisible)
.onAppear {
withAnimation(.spring().delay(0.3)) {
isVisible = true
}
}
}
}
Затримка 0,3 секунди створює ефект послідовної появи, якщо на екрані кілька таких карток. Для списку анімованих елементів використовуйте індекс елемента як множник затримки.
.task — модифікатор SwiftUI, доданий у iOS 15, який вирішує проблему асинхронних операцій в onAppear. На відміну від onAppear, .task приймає async-замикання, автоматично керує його життєвим циклом і скасовує при зникненні View. Якщо onAppear виконується синхронно, то .task запускає асинхронну операцію та дозволяє SwiftUI скасувати її при onDisappear.
Головна відмінність — керування скасуванням. Коли .task створює async-операцію, SwiftUI зберігає посилання на Task і автоматично викликає cancel() при видаленні View з ієрархії. onAppear з Task {} всередині не скасовує запущену операцію — вона продовжує виконуватися навіть після того, як View зникла, що може викликати стан гонки або запис у вже звільнений екземпляр.
| Характеристика | .onAppear | .task |
|---|---|---|
| Версія iOS | iOS 13+ | iOS 15+ |
| Підтримка async | Тільки через Task {} | Нативний async/await |
| Автоскасування | Ні | При зникненні View |
| Повторний виклик | При кожній появі | Одноразово за замовчуванням |
| Синхронний код | Так | Тільки async |
Вибір модифікатора: для синхронних дій (анімації, аналітика, логування) використовуйте onAppear. Для асинхронного завантаження даних (API, Core Data, файлова система) переважніше .task — він безпечніший і чистіший.
Помилка 1: множинний виклик через перестворення View. Коли SwiftUI перестворює тіло View (зміна @State, поворот екрана), onAppear може викликатися повторно. Рішення — додати прапорець завантаження або використовувати .equatable() для запобігання зайвих перемальовувань. За даними SwiftLee (2025), 40% багів SwiftUI у продакшені пов'язані саме з повторними викликами onAppear.
Помилка 2: витік пам'яті через сильне посилання. Якщо замикання onAppear захоплює self без слабкого посилання, створюється retain cycle з View. SwiftUI не гарантує обнулення захоплених об'єктів при зникненні View. Використовуйте capture list [weak self] для ViewModel або сервісів.
Помилка 3: виконання у фоновому потоці. onAppear виконується в основному потоці — це правильно для UI-операцій. Але якщо всередині onAppear запускається Task, переконайтеся, що оновлення @State відбувається через MainActor.run. Swift 5.9 і вище автоматично повертається на MainActor, але краще вказати @MainActor явно.
Патерн з прапорцем завантаження — найнадійніший спосіб захиститися від дублювання. Зберігайте прапорець у @State або @StateObject і скидайте його лише при ручному оновленні. Альтернатива — використання .task замість onAppear: .task за замовчуванням не перезапускається при перемальовуванні, якщо async-операція вже виконується.
struct SafeView: View {
@State private var hasAppeared = false
@State private var items: [Item] = []
var body: some View {
List(items, id: \.id) { item in
Text(item.name)
}
.onAppear {
guard !hasAppeared else { return }
hasAppeared = true
Task {
items = await DataService.shared.fetchItems()
}
}
}
}
Часті запитання
viewDidLoad викликається один раз за час життя UIViewController, незалежно від видимості. .onAppear викликається при кожному додаванні View в ієрархію — якщо View видаляється і знову додається, onAppear спрацьовує заново. У NavigationView viewDidLoad викликається при ініціалізації, а onAppear — при кожному показі екрана.
Так, через обгортку Task { await asyncFunction() }. Однак для async-операцій краще використовувати .task, який автоматично керує скасуванням і не потребує створення Task вручну. .task також гарантує скасування при зникненні View, запобігаючи витокам.
Причина — перестворення тіла View через зміну @State, @Published або конфігурації предка. SwiftUI може перемалювати View у відповідь на зміну будь-якої спостережуваної властивості. Додатково LazyVStack і List викликають onAppear для комірок, які наближаються до видимої області, і повторно при скролі вгору.
Так, .onAppear доступний на всіх платформах SwiftUI: iOS 13+, watchOS 6+, tvOS 13+, macOS 10.15+. Поведінка ідентична: модифікатор викликається при додаванні View в ієрархію. На watchOS onAppear спрацьовує при активації застосунку зі стану очікування, що потребує врахування в дизайні.
.onAppear не приймає параметрів — тільки замикання Void. Для передачі параметрів використовуйте замикання, яке захоплює зовнішні змінні. Альтернативний підхід — створити кастомний модифікатор onAppear з параметрами через ViewModifier або аналог .onChange.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також