Non-Fatal Error — е грешка, която не води до прекратяване на работата на приложението и позволява продължаване на изпълнението на програмата. За разлика от fatal error, нефаталните грешки могат да бъдат прихванати, обработени и логвани без загуба на потребителската сесия. Според данни на Firebase Crashlytics Documentation, 2024, около 70% от всички регистрирани грешки в production приложенията са нефатални, но тяхното игнориране води до натрупване на технически дълг и постепенно влошаване на потребителското изживяване. Коректната обработка на non-fatal грешки е едно от ключовите умения на мобилния разработчик.
Основни моменти
Non-Fatal Error — е изключение или грешно състояние, което не причинява прекратяване на процеса. Приложението продължава да работи, но може да бъде в неправилно състояние: данните не се заредиха, заявката не се изпрати, елементът на интерфейса не се показа. Потребителят или не забелязва грешката, или вижда съобщение и продължава да използва приложението.
Нефаталната грешка винаги оставя на програмата път за възстановяване. Обработчикът на грешки може да предложи алтернативни данни, да повтори операцията или да покаже placeholder на интерфейса. Основната задача е да се предотврати crash и да се поддържа потребителското изживяване на приемливо ниво. Разработчикът трябва изрично да предвиди сценарий за възстановяване във всеки catch блок.
Според данни на Instabug 2024, 65% от потребителите изтриват приложението след две неуспешни взаимодействия. Non-fatal грешките, оставени без внимание, се натрупват и намаляват общото качество на работа. Систематичното логване и коригиране на нефатални грешки е пряк път към повишаване на задържането и подобряване на потребителските оценки в магазините за приложения.
Мрежови грешки — най-често срещаният тип 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 може да зависи от имплементацията. Мрежов timeout в едно приложение се обработва като non-fatal (повтаряне на заявката след 1–2 секунди), в друго може да бъде фатален (crash при липса на обработчик). Качествената обработка на грешки превръща потенциално фаталните ситуации в нефатални, повишавайки стабилността на приложението. Проектирането на система за обработка на грешки е една от ключовите архитектурни задачи при разработката на мобилно приложение с високи изисквания за надеждност. Вградената система за мониторинг позволява на екипа бързо да открива и отстранява нефаталните грешки, преди да са повлияли на значителен брой потребители.
Firebase Crashlytics — основният инструмент за логване на нефатални грешки в мобилни приложения. Методът recordException позволява записване на non-fatal изключение с пълен stack trace и контекст на изпълнение, без да прекъсва работата на приложението. За разлика от 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-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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също