Non-Fatal Error — خطایی است که منجر به پایان کار برنامه نمیشود و امکان ادامه اجرای برنامه را فراهم میکند. برخلاف fatal error، خطاهای غیربحرانی میتوانند بدون از دست دادن جلسه کاربر گرفته، مدیریت و لاگ شوند. طبق دادههای Firebase Crashlytics Documentation, 2024، حدود 70٪ از تمام خطاهای ثبتشده در برنامههای production غیربحرانی هستند، اما نادیده گرفتن آنها منجر به انباشت بدهی فنی و بدتر شدن تدریجی تجربه کاربری میشود. مدیریت صحیح خطاهای non-fatal یکی از مهارتهای کلیدی توسعهدهنده موبایل است.
نکات اصلی
Non-Fatal Error — یک استثنا یا حالت خطایی است که باعث خاتمه فرآیند نمیشود. برنامه به کار خود ادامه میدهد، اما ممکن است در وضعیت نادرستی قرار گیرد: دادهها بارگیری نشدند، درخواست ارسال نشد، عنصر رابط کاربری نمایش داده نشد. کاربر یا متوجه خطا نمیشود یا پیامی را میبیند و به استفاده از برنامه ادامه میدهد.
خطای غیربحرانی همیشه راهی برای بازیابی در اختیار برنامه قرار میدهد. مدیریتکننده خطا میتواند دادههای جایگزین ارائه دهد، عملیات را تکرار کند یا یک placeholder رابط کاربری نشان دهد. وظیفه اصلی جلوگیری از crash و حفظ تجربه کاربری در سطح قابل قبول است. توسعهدهنده باید صریحاً سناریوی بازیابی را در هر بلوک catch پیشبینی کند.
طبق دادههای Instabug 2024، 65٪ از کاربران پس از دو تعامل ناموفق برنامه را حذف میکنند. خطاهای non-fatal که بدون توجه رها میشوند، انباشته شده و کیفیت کلی کار را کاهش میدهند. لاگ کردن سیستماتیک و رفع خطاهای غیربحرانی مسیری مستقیم برای افزایش retention و بهبود امتیازات کاربران در فروشگاههای برنامه است.
خطاهای شبکه — رایجترین نوع خطاهای non-fatal در برنامههای موبایل. زمان انتظار اتصال، قطع شبکه، کد وضعیت نادرست سرور — همه این موارد بدون crash گرفته و مدیریت میشوند. به کاربر پیامی درباره عدم دسترسی به سرویس با پیشنهاد تلاش مجدد نمایش داده میشود. برای خطاهای شبکه، الگوی retry با تأخیر نمایی معمول است.
فرمت نادرست پاسخ سرور، عدم وجود فیلد اجباری، نوع داده نادرست — خطاهای تجزیه اگر برنامه دادههای نادرست را به درستی مدیریت کند، غیربحرانی هستند. رویکرد معمول استفاده از مقادیر پیشفرض جایگزین و لاگ کردن خطای تجزیه با زمینه درخواست برای تحلیل بعدی در سرور است.
مشکلات بارگیری تصاویر، فونتهای نادرست، خطاهای layout — همه اینها بحرانی نیستند، اما تجربه کاربر را بدتر میکنند. تصاویر جایگزین و مقادیر fallback از نمایش صفحههای خالی جلوگیری کرده و خطاها را کمتر قابل توجه میکنند. در React Native برای خطاهای UI از Error Boundary با نمایش کامپوننت جایگزین استفاده میشود.
خطا در محاسبات، ناهماهنگی حالتها، انتقالهای نادرست بین صفحهها — خطاهای منطقی اغلب منجر به crash نمیشوند، اما باعث رفتار نادرست برنامه میشوند. تشخیص آنها بدون لاگ کردن و نظارت سیستماتیک دشوارتر است، زیرا گزارش crash ایجاد نمیکنند و تا شکایت کاربر نادیده میمانند.
Non-Fatal Error با fatal تفاوت دارد از این جهت که به برنامه امکان ادامه کار میدهد. Fatal error — حالتی است که برنامه نمیتواند از آن بازیابی کند: dereference اشاره گر 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 مدیریت میشود (تکرار درخواست پس از ۱–۲ ثانیه)، در دیگری ممکن است fatal باشد (crash در صورت نبود مدیریتکننده). مدیریت باکیفیت خطاها، موقعیتهای بالقوه fatal را به غیربحرانی تبدیل کرده و پایداری برنامه را افزایش میدهد. طراحی سیستم مدیریت خطا یکی از وظایف معماری کلیدی در توسعه برنامه موبایل با الزامات بالای قابلیت اطمینان است. سیستم نظارت داخلی به تیم امکان میدهد خطاهای غیربحرانی را قبل از تأثیر بر تعداد قابل توجهی از کاربران به سرعت کشف و رفع کند.
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. SDK Sentry متد captureException را ارائه میدهد که جزئیات استثنا را به سرور ارسال میکند. مزیت کلیدی Sentry گروهبندی خطاهای non-fatal مشابه در یک issue، تحلیل فراوانی تکرار و زمینه اجرا به صورت breadcrumbs — توالی اقدامات کاربر قبل از خطا — است.
همه خطاهای non-fatal نیازی به لاگ شدن ندارند. حالات مورد انتظار — عدم وجود شبکه هنگام قطع اتصال — میتوانند انتخابی لاگ شوند. خطاهای غیرمنتظره — NullPointerException در کد مدیریتشده، فرمت نادرست داده، logic error — باید همیشه لاگ شوند. هر تیم آستانه اهمیت را تعیین میکند: به طور متوسط ۱۰ تا ۲۰ خطای non-fatal منحصربهفرد به ازای ۱۰۰۰ کاربر در روز نرمال در نظر گرفته میشود. مهم است که هشدارهایی برای افزایش ناگهانی تعداد خطاهای 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 — یک استثنای زمان اجراست که قبلاً رخ داده اما منجر به 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 در هر سطح از escalation آنها به سطح fatal جلوگیری میکند.
در iOS خطاهای non-fatal از طریق do-catch با throw مدیریت میشوند، در Android — از طریق try-catch با استثناها. iOS از NSError با دامنهها و کدهای خطا استفاده میکند، Android — از استثناهای Java/Kotlin. Crashlytics در هر دو پلتفرم به طور یکسان از طریق recordException کار میکند و رابط یکپارچهای برای نظارت فراهم میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید