Non-Fatal Error — це помилка, яка не призводить до завершення роботи додатку і дозволяє продовжити виконання програми. На відміну від fatal error, нефатальні помилки можуть бути перехоплені, оброблені та залоговані без втрати сеансу користувача. За даними Firebase Crashlytics Documentation, 2024, близько 70% всіх зареєстрованих помилок у production-додатках є нефатальними, але їх ігнорування веде до накопичення технічного боргу та поступового погіршення користувацького досвіду. Коректна обробка non-fatal помилок — одна з ключових навичок мобільного розробника.
Головне
Non-Fatal Error — це виняток або помилковий стан, який не викликає завершення процесу. Додаток продовжує працювати, але може знаходитися в некоректному стані: не завантажилися дані, не відправився запит, не відобразився елемент інтерфейсу. Користувач або не помічає помилку, або бачить повідомлення та продовжує використання додатку.
Нефатальна помилка завжди залишає програмі шлях для відновлення. Обробник помилок може запропонувати альтернативні дані, повторити операцію або показати заглушку інтерфейсу. Основне завдання — не допустити crash та зберегти користувацький досвід на прийнятному рівні. Розробник повинен явно передбачити сценарій відновлення в кожному catch-блоці.
За даними Instabug 2024, 65% користувачів видаляють додаток після двох невдалих взаємодій. Non-fatal помилки, залишені без уваги, накопичуються та знижують загальну якість роботи. Систематичне логування та виправлення нефатальних помилок — прямий шлях до підвищення retention та покращення користувацьких оцінок у магазинах додатків.
Мережеві помилки — найпоширеніший тип non-fatal помилок у мобільних додатках. Таймаут з'єднання, втрата мережі, невірний статус-код сервера — всі ці ситуації перехоплюються та обробляються без crash. Користувачеві показується повідомлення про недоступність сервісу з пропозицією повторити спробу. Для мережевих помилок типовий патерн retry з експоненційною затримкою.
Невірний формат відповіді від сервера, відсутність обов'язкового поля, некоректний тип даних — помилки парсингу є нефатальними, якщо додаток коректно обробляє некоректні дані. Типовий підхід — використання запасних значень за замовчуванням та логування помилки парсингу з контекстом запиту для подальшого аналізу на сервері.
Проблеми із завантаженням зображень, некоректні шрифти, помилки в layout — всі вони не фатальні, але погіршують враження користувача. Placeholder-зображення та fallback-значення дозволяють уникнути порожніх екранів і роблять помилки менш помітними. У React Native для UI-помилок використовують Error Boundary з відображенням запасного компонента.
Помилки в розрахунках, невідповідність станів, неправильні переходи між екранами — логічні помилки часто не призводять до crash, але призводять до некоректної поведінки додатку. Їх складніше виявити без систематичного логування та моніторингу, оскільки вони не створюють crash-репорт і залишаються непоміченими до скарги користувача.
Non-Fatal Error відрізняється від fatal тим, що залишає програмі можливість продовжувати роботу. Fatal error — це стан, з якого додаток не може відновитися: розіменування null-покажчика, переповнення стеку, нестача пам'яті. Non-fatal помилку можна перехопити, обробити та продовжити виконання, в той час як fatal error вимагає перезапуску додатку.
| Характеристика | Non-Fatal Error | Fatal Error |
|---|---|---|
| Завершення додатку | Ні | Так |
| Можливість відновлення | Так, через catch-блок | Ні |
| Логування | З коду через recordException | Тільки crash-репортером |
| UX-вплив | Тимчасове незручність | Повний збій сеансу |
| Приклад | Network timeout, parse error | NullPointerException, OOM |
Межа між non-fatal та fatal може залежати від реалізації. Мережевий таймаут в одному додатку обробляється як non-fatal (повтор запиту через 1–2 секунди), в іншому може бути фатальним (crash за відсутності обробника). Якісна обробка помилок перетворює потенційно фатальні ситуації в нефатальні, підвищуючи стабільність додатку. Проектування системи обробки помилок — одне з ключових архітектурних завдань при розробці мобільного додатку з високими вимогами до надійності. Вбудована система моніторингу дозволяє команді швидко виявляти та усувати нефатальні помилки до того, як вони вплинуть на значну кількість користувачів.
Firebase Crashlytics — основний інструмент для логування нефатальних помилок у мобільних додатках. Метод recordException дозволяє зафіксувати non-fatal виняток з повним стектрейсом та контекстом виконання, не перериваючи роботу додатку. На відміну від crash-репортів, recordException можна викликати в будь-якому місці коду для логування перехоплених винятків.
fun fetchUserData(userId: String) {
try {
val response = apiService.getUser(userId)
updateUI(response)
} catch (e: IOException) {
Crashlytics.log("Network error for user $userId")
Crashlytics.recordException(e)
showRetryDialog()
} catch (e: JsonParseException) {
// Non-fatal: використовуємо запасні дані
Crashlytics.recordException(e)
showFallbackContent()
}
}
// Логування з користувацькими ключами
Crashlytics.setCustomKey("screen", "Profile")
Crashlytics.setCustomKey("api_version", "v3")
Sentry — альтернатива Crashlytics з більш детальною діагностикою non-fatal помилок. Sentry SDK надає метод captureException, який відправляє деталі винятку на сервер. Ключова перевага Sentry — групування схожих non-fatal помилок в один issue, аналіз частоти повторення та контекст виконання у вигляді breadcrumbs — послідовності дій користувача перед помилкою.
Не всі non-fatal помилки потрібно логувати. Очікувані стани — відмова мережі за відсутності з'єднання — можна логувати вибірково. Неочікувані помилки — NullPointerException в обробленому коді, невірний формат даних, logic error — повинні логуватися завжди. Кожна команда визначає поріг значущості: в середньому від 10 до 20 унікальних non-fatal помилок на 1000 користувачів на день вважається нормою. Важливо налаштувати алерти на різке зростання числа non-fatal помилок — це може вказувати на проблеми з новою версією API або регресію після релізу.
Базовий механізм обробки — try-catch, який перехоплює виняток та виконує код відновлення. Для мережевих операцій типовий патерн — повтор запиту з експоненційною затримкою (retry with backoff). Для помилок парсингу — використання запасних значень за замовчуванням та логування контексту для подальшого аналізу на серверній стороні.
func loadImage(from url: URL) -> UIImage? {
do {
let data = try Data(contentsOf: url)
return UIImage(data: data)
} catch {
Logger.shared.logError(error: "Image load failed: \(url)")
return UIImage(named: "placeholder")
}
}
func performRequest() async throws -> Data {
var lastError: Error? = nil
for attempt in 0..<3 {
do {
return try await URLSession.shared.data(from: url)
} catch {
lastError = error
try await Task.sleep(UInt64(pow(2, attempt)) * 1_000_000_000)
}
}
throw lastError ?? URLError(.unknown)
}
Result-типи — альтернативний підхід без винятків. Функція повертає sealed class Result з варіантами Success та Failure. Викликаючий код обробляє обидва варіанти явно, що виключає необроблені помилки. Result-типи популярні в Kotlin (Result
Для кожного типу non-fatal помилки потрібно передбачити стратегію відновлення: завантаження кешованих даних при мережевій помилці, використання значень за замовчуванням при помилці парсингу, повторна ініціалізація компонента при UI-помилці. Хороша практика — показувати користувачеві toast або snackbar з повідомленням про помилку, але не блокувати взаємодію з додатком повністю. Важливо розрізняти recoverable (відновлювані) та non-recoverable помилки — для других стратегія відновлення буде іншою, наприклад, пропозиція перезапустити екран або очистити дані. Кешування попереднього успішного стану часто виявляється найпростішим і найбільш ефективним способом обробки non-fatal помилок на мобільних платформах.
Поширені запитання
Warning — це попередження компілятора або статичного аналізатора про потенційну проблему в коді. Non-fatal error — це runtime-виняток, який вже стався, але не призвів до crash. Warning можна усунути до компіляції, non-fatal error — обробити під час виконання через catch-блок.
Ні, надмірне логування зашумлює моніторинг. Варто логувати неочікувані помилки в production та ігнорувати очікувані стани: відмова мережі при відсутності з'єднання логується вибірково, а NullPointerException в обробленому коді — завжди. Кожна команда визначає поріг значущості виходячи з контексту додатку.
У SwiftUI використовується ObservableObject з @Published полем errorState для відстеження помилкового стану. View підписується на зміни та відображає альтернативний контент. До iOS 17 застосовувався Combine з обробниками, починаючи з iOS 17 — SwiftData та @Observable макроси для реактивного оновлення UI.
Так, якщо помилка викликає ланцюгову реакцію. Приклад: нефатальний збій завантаження зображення може призвести до некоректного стану UI, який потім викликає crash при спробі відображення. Якісна обробка non-fatal помилок на кожному рівні запобігає їх ескалації до фатального рівня.
В iOS non-fatal помилки обробляються через do-catch з throw, в Android — через try-catch з винятками. iOS використовує NSError з доменами та кодами помилок, Android — Java/Kotlin винятки. Crashlytics працює однаково на обох платформах через recordException, надаючи єдиний інтерфейс для моніторингу.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також