Fatal Error — це критична помилка, яка призводить до негайного завершення роботи додатка (crash). На відміну від non-fatal error, фатальна помилка не залишає програмі можливості для відновлення — процес аварійно завершується операційною системою або середовищем виконання. За даними Firebase Crashlytics 2024, середній додаток втрачає 2,5% користувачів після кожного crash, а усунення фатальних помилок є пріоритетом номер один у мобільній розробці. Чим вищий crash-free rate, тим вищий рейтинг додатка в магазинах і тим менший відтік користувачів.
Головне
Fatal Error — це помилка, за якої подальше виконання програми неможливе. Операційна система або віртуальна машина завершує процес, щоб запобігти пошкодженню даних. В iOS фатальна помилка викликає сигнал SIGABRT або SIGSEGV, в Android — необроблений виняток, який досягає кореневого обробника та завершує процес. Додаток миттєво закривається, користувач повертається на Home Screen.
Характерні ознаки фатальної помилки: crash-репорт з повним стектрейсом, несподіване зникнення додатка, запис у system log про завершення процесу, чорний або білий екран перед закриттям. Користувач бачить екран Home без можливості відновити сеанс — додаток доводиться запускати заново з нульового стану. В iOS crash супроводжується записом у файл .crash, доступний через Xcode Organizer.
Кожен crash негативно впливає на утримання користувачів. За даними Google Play Console 2024, додатки з crash-free rate нижче 99,5% отримують знижений рейтинг у пошуку та рекомендаціях. Crash-rate є одним із ключових сигналів якості для App Store та Google Play — високий рівень фатальних помилок може заблокувати публікацію оновлень. Для фінансових та медичних додатків crash-free rate нижче 99,9% вважається неприпустимим.
Null-pointer dereference — провідна причина фатальних помилок у мобільних додатках. Спроба звернутися до властивості або методу об'єкта, який дорівнює null, викликає NullPointerException в Android або EXC_BAD_ACCESS в iOS. За даними JetBrains 2023, близько 28% усіх production-крашів пов'язані з null-покажчиками. В Kotlin null-safety система значно знижує цей відсоток, але force unwrap та Java-сумісність залишаються джерелами проблеми.
Звернення до елемента колекції за неіснуючим індексом — друга за частотою причина crash-ів. В Java та Kotlin це ArrayIndexOutOfBoundsException, в Swift — fatal error: Index out of range. Найчастіше виникає при роботі зі списками після фільтрації або динамічної зміни розміру колекції. Використання безпечних методів getOrNull (Kotlin) або indices.contains (Swift) запобігає цьому типу фатальних помилок.
Нестача пам'яті (OutOfMemoryError), переповнення стеку (StackOverflowError), завантаження неіснуючого ресурсу — resource-помилки часто фатальні та складно відтворювані. OutOfMemoryError виникає при завантаженні великих зображень без стиснення або при витоку пам'яті через незвільнені посилання. StackOverflowError — при глибокій рекурсії без базового випадку або при циклічних викликах у ланцюжку делегатів.
Deadlock, race condition, модифікація колекції під час ітерації — багатопотокові помилки проявляються недетерміновано і є найскладнішими для діагностики. В Android ConcurrentModificationException при модифікації ArrayList з різних потоків, в iOS crash через модифікацію NSMutableArray без синхронізації. Використання корутин Kotlin (structured concurrency) або Swift Actors (iOS 16+) знижує ймовірність concurrency-крашів.
Ключова відмінність — можливість відновлення. Non-Fatal Error дозволяє програмі продовжити роботу: мережевий таймаут обробляється try-catch, помилка парсингу замінюється значенням за замовчуванням. Fatal Error не має такого шляху — crash неминучий, і додаток має бути перезапущено. Межа між цими типами помилок визначається архітектурою додатка.
| Характеристика | Fatal Error | Non-Fatal Error |
|---|---|---|
| Завершення додатка | Так | Ні |
| Відновлення | Неможливо | Можливо через catch-блок |
| Збір інформації | Тільки crash-репортер | Логування з коду |
| UX-шкода | Повний збій сеансу | Тимчасові незручності |
| Типовий приклад | NullPointerException | IOException |
Одна й та сама помилка може бути fatal на одній платформі та non-fatal на іншій. Ділення на нуль в Java/Kotlin викидає ArithmeticException (не фатально — можна перехопити), в Swift викликає fatal error: Division by zero (краш без можливості перехоплення). Розробник повинен враховувати поведінку конкретної мови та середовища виконання при проєктуванні обробки помилок. Розуміння межі між fatal та non-fatal — основа побудови відмовостійкої архітектури мобільного додатка.
Firebase Crashlytics — стандарт де-факто для діагностики crash-ів у мобільних додатках. SDK автоматично збирає стектрейс, стан пристрою, версію ОС та логи безпосередньо перед crash-ем. Dashboard групує ідентичні краші в один issue, показуючи кількість постраждалих користувачів, частоту повторення та версію додатка, в якій відбувся crash.
// Ініціалізація Crashlytics в Android додатку
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
Crashlytics.setCustomKey("build_type", "production")
}
}
// Встановлення користувацьких даних для діагностики crash
Crashlytics.setUserId("user_12345")
Crashlytics.setCustomKey("screen", "ProfileFragment")
Crashlytics.setCustomKey("api_response", responseCode)
// Примусовий crash для тестування інтеграції
Crashlytics.crash()
Sentry — альтернатива більш детальною діагностикою. Sentry показує не лише стектрейс, але й стан усіх змінних, послідовність подій до помилки та контекст виконання. Breadcrumbs Sentry дозволяють відновити ланцюжок дій користувача перед фатальною помилкою: натискання кнопок, переходи між екранами, мережеві запити. Sentry також надає моніторинг продуктивності та сесій для комплексного аналізу якості.
Для коректної діагностики crash-ів на iOS потрібне завантаження dSYM-файлів (debug symbols) у Crashlytics або Sentry. Без dSYM стектрейс міститиме лише адреси пам'яті замість імен функцій. Для Android потрібне завантаження mapping-файлів при використанні ProGuard або R8. Автоматизація завантаження dSYM через build phase в Xcode або Gradle-плагін обов'язкова для production-збірок.
Базовий метод запобігання — safe unwrapping усіх опціональних та nullable-значень. Використання if-let в Swift та let з ?: в Kotlin виключає null-pointer помилки. Жодного force unwrap без гарантії наявності значення. Компілятори Kotlin та Swift обидва попереджають про потенційно небезпечні операції — ці попередження не можна ігнорувати в production-коді.
// ЗАПОБІГАННЯ fatal error через safe unwrapping
func processUser(id: String) -> String {
guard let user = database.findUser(by: id) else {
return "User not found"
}
guard let email = user.email else {
return "Email not set"
}
return email
}
// Безпечне звернення до елементів колекції
func safeGet <T>(items: [T], index: Int) -> T? {
guard items.indices.contains(index) else { return nil }
return items[index]
}
// Перевірка меж масиву перед зверненням
let numbers = [1, 2, 3]
if numbers.indices.contains(5) {
print(numbers[5])
} else {
print("Index out of range")
}
Defensive programming — другий рівень захисту. Завжди перевіряйте вхідні параметри функцій, повертайте Optional або Result замість force unwrap, використовуйте assert в debug-збірках для раннього виявлення помилок на етапі розробки. Unit-тести на граничні випадки (null, порожні колекції, некоректні індекси) повинні покривати всі публічні точки входу в бізнес-логіку додатка.
В React Native та SwiftUI можна встановити error boundary — компонент, який перехоплює фатальні помилки рендерингу та показує fallback UI замість crash. Це перетворює фатальну UI-помилку на non-fatal з точки зору користувацького досвіду — додаток продовжує працювати, а користувач бачить повідомлення про помилку в конкретному блоці інтерфейсу, а не білий екран.
Інтеграція автоматичних перевірок у CI/CD пайплайн: статичний аналіз (Detekt для Kotlin, SwiftLint для Swift), запуск UI-тестів на реальних пристроях, перевірка crash-free rate в тестовому середовищі. Блокування мержу при перевищенні порогу crash-rate (рекомендований поріг — більше 0,1% нових crash-ів на коміт).
Часті запитання
Ні, після fatal error відновлення неможливе — процес завершується на рівні ОС. Єдиний спосіб — запобігти фатальній помилці до її виникнення через безпечні конструкції, defensive programming та всебічне тестування граничних випадків на етапі розробки.
Segfault (SIGSEGV) — один із видів fatal error, що виникає при зверненні до неприпустимої області пам'яті. FATAL ERROR — загальне поняття для всіх невідновлюваних помилок, включаючи segfault, abort, stack overflow, out of memory та необроблені винятки в runtime.
Інтеграція Crashlytics (Firebase) або Sentry SDK автоматично збирає всі необроблені винятки. SDK перехоплює сигнали ОС та runtime-винятки, формує crash-репорт зі стектрейсом та контекстом і відправляє його на сервер при наступному запуску додатка.
Для тестування обробки crash-ів використовується force crash у debug-збірці. Crashlytics надає метод crash() для імітації фатальної помилки. В unit-тестах перевіряється коректність guard та if-let, а UI-тести покривають граничні випадки введення даних та стану інтерфейсу.
Ні, лише необроблені винятки стають фатальними. Виняток, перехоплений try-catch, є non-fatal. Різниця між обробленим та необробленим винятком визначає, чи завершиться додаток чи продовжить роботу з альтернативним станом при мінімальній шкоді для користувацького досвіду.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також