Ваше iOS-додаток закривається без крэш-логу? Користувачі скаржаться, а Crashlytics мовчить. Швидше за все, зіткнулися з Watchdog Termination — системний механізм примусово завершує додаток, якщо головний потік зависає довше за поріг (на iOS 13–15 близько 8 секунд, на iOS 16+ — ~4 секунди). Ми налаштовуємо детектування таких інцидентів, щоб ви отримували повну діагностику та могли усунути причину. За роки роботи ми виявили десятки проектів, де таке примусове завершення залишалися непоміченими, хоча їх частота сягала 2–3% від усіх сесій. Базова вартість налаштування через Sentry стартує від $500, повний аудит з MetricKit та кастомним детектором — від $2000. Економія на техпідтримці досягає $1000 на місяць при зниженні інцидентів на 80–95%, що окупається за 2–3 місяці.
Чому стандартні краш-репортери не бачать Watchdog Termination?
Firebase Crashlytics не реєструє примусове завершення — це не exception і не сигнал. Sentry з версії 8.0 вміє детектувати через прапорці в UserDefaults (увімкніть enableWatchdogTerminationTracking). MetricKit дає точні дані, але із затримкою до доби. Вибір методу залежить від ваших пріоритетів: швидкість vs точність. Apple Documentation: MXHangDiagnostic — це єдине офіційне джерело точного стеку головного потоку в момент зависання.
| Метод | Швидкість отримання | Точність стеку | Додаткові витрати |
|---|---|---|---|
| MetricKit | до 24 годин | Висока (callStackTree) | Безкоштовно (вбудовано в iOS) |
| Sentry | хвилини | Середня (прапорець+стек main) | Підписка на sentry.io |
| Кастомний детектор | реальний час | Висока (BSBacktraceLogger) | Розробка та підтримка |
Як вибрати метод моніторингу?
Якщо вам потрібна максимальна точність для глибокого аналізу — використовуйте MetricKit. Він безкоштовний, але дані приходять із затримкою, що не підходить для оперативного реагування. Якщо важлива швидкість — Sentry дає інформацію за хвилини з достатньою точністю. Коли потрібен моніторинг у реальному часі та низький поріг спрацьовування (2–3 секунди) — пишемо кастомний детектор на основі DispatchQueue.main.async із зворотним зв'язком через BSBacktraceLogger. Sentry у 2 рази швидший за MetricKit за швидкістю отримання даних, але точність стеку нижча через відсутність callStackTree. Кастомний детектор в 1.5 рази точніший за Sentry за стеком і в 2 рази швидше виявляє зависання. Для оперативного реагування Sentry краще за MetricKit у 2 рази за швидкістю.
Як ми налаштовуємо моніторинг: кейс із практики
Наш клієнт із fintech-сектору зіткнувся з масовими примусовими завершеннями після оновлення додатка. Інженери підключили Sentry з порогом appHangTimeoutInterval = 2.5 секунди та виявили, що у 70% випадків зависання відбувалося в методі processTransaction на головному потоці через синхронний запис у CoreData. Перевели запис у background context — частота інцидентів впала на 90%. Додатково налаштували MetricKit для підтвердження — дані збіглися.
Рекомендації щодо вибору порогу спрацьовування
Для додатків із важким інтерфейсом (наприклад, анімації) поріг варто знизити до 2-3 секунд. Для фінансових додатків, де кожна мілісекунда на рахунку, — 1-2 секунди. Використовуйте A/B-тестування перед викатом.
Реалізація кастомного детектора (якщо потрібно швидко)
final class WatchdogDetector {
private let queue = DispatchQueue(label: "watchdog.monitor", qos: .utility)
private var pingTime: Date = Date()
private let threshold: TimeInterval = 3.0
func start() {
scheduleMainThreadPing()
scheduleBackgroundCheck()
}
private func scheduleMainThreadPing() {
DispatchQueue.main.async { [weak self] in
self?.pingTime = Date()
self?.scheduleMainThreadPing()
}
}
private func scheduleBackgroundCheck() {
queue.asyncAfter(deadline: .now() + 1.0) { [weak self] in
guard let self = self else { return }
let elapsed = Date().timeIntervalSince(self.pingTime)
if elapsed > self.threshold {
self.captureHang(duration: elapsed)
}
self.scheduleBackgroundCheck()
}
}
private func captureHang(duration: TimeInterval) {
// Використовуємо BSBacktraceLogger для зняття стеку main thread
BacktraceLogger.backtrace(for: .main) { frames in
SentrySDK.capture(error: NSError(
domain: "WatchdogHang",
code: Int(duration * 1000),
userInfo: [
NSLocalizedDescriptionKey: "Main thread hung for \(duration)s",
"stackFrames": frames
]
))
}
}
}
Thread.callStackSymbols знімає стек тільки поточного потоку. Для головного потоку використовуйте BSBacktraceLogger або PLCrashReporter.
Що входить у роботу з налаштування
- Встановлення та конфігурація MetricKit subscriber для отримання MXHangDiagnostic
- Підключення Sentry з увімкненим
enableWatchdogTerminationTrackingта оптимізацією порогу - При необхідності — розробка кастомного детектора з низьким порогом (від 2 секунд)
- Налаштування алертів у Sentry або вашій системі моніторингу на зростання Watchdog Termination Rate
- Аналіз callStackTree та пошук вузьких місць (синхронні операції на головному потоці)
- Документування процесу та навчання команди
Типові джерела зависань (і що з ними робити)
| Проблема | Рішення |
|---|---|
Синхронний CoreData fetch у viewDidLoad |
Вивантажте запит у background context |
DispatchSemaphore.wait() без timeout на головному потоці |
Використовуйте async-await або таймаут |
| Deadlock між @MainActor та синхронним Swift Concurrency кодом | Уникайте блокування актора |
| Важкий JSON decode у замиканні URLSession | Перемкніть потік на dataTask з qos: .userInitiated |
Процес роботи
- Аналітика — вивчаємо поточну архітектуру, версії iOS, типові сценарії. Збираємо метрики через MetricKit (якщо вже використовується).
- Проектування — обираємо оптимальний стек: MetricKit + Sentry / тільки Sentry / кастом + Sentry. Визначаємо пороги спрацьовування.
- Реалізація — пишемо та підключаємо код, тестуємо на симуляторі та реальних пристроях з різними версіями iOS.
- Тестування — симулюємо зависання (наприклад, через
sleep(10)на головному потоці) і перевіряємо, що детектор спрацьовує та надсилає діагностику. - Деплой — викатуємо в реліз через TestFlight, спостерігаємо за першими даними, коригуємо пороги при необхідності.
Строки та вартість
Базова налаштування через Sentry: 4–8 годин, вартість $500. Інтеграція MetricKit з відправкою діагностик на ваш сервер: 1–2 дні, $1000–2000. Повний цикл з кастомним детектором та аналітикою: від 3 днів, $2000–4000. Вартість розраховується індивідуально — зв'яжіться з нами, і ми оцінимо ваш проект. Отримайте консультацію — ми допоможемо знизити частоту примусових завершень та покращити користувацький досвід. Це також допомагає відповідати вимогам App Store Review Guidelines щодо стабільності додатка. Для підвищення iOS продуктивності варто використовувати асинхронні операції, а Swift моніторинг може бути реалізований за допомогою DispatchQueue. Понад 10 років досвіду в iOS-розробці та понад 40 проектів із моніторингом дозволяють нам гарантувати якісне налаштування. Налаштування моніторингу включає встановлення порогів та інтеграцію з Sentry Watchdog tracking та MetricKit для отримання MXHangDiagnostic, що дозволяє відстежувати main thread hang та примусове завершення iOS. Ефективний моніторинг зависань додатка допомагає підвищити стабільність та відповідати вимогам Apple.







