تكرار السؤال: …
الإجابة المختصرة
باستخدام Sealed Interface أو Result<T> يمثل النجاح مع البيانات أو الفشل مع نوع الخطأ، مما يجبر المستهلك على معالجة كل السيناريوهات.
الشرح
بدلاً من ترك استثناءات IOException و HttpException ترمى عشوائيًا وتصل للواجهة، يغلف الـ Repository استدعاءات الشبكة داخل كتلة آمنة. هذا يحول أخطاء الشبكة الشائعة (مثل عدم توفر إنترنت، انتهاء المهلة، أو خطأ 404/500) إلى حالات واضحة تترجمها الواجهة إلى رسائل مفهومة للمستخدم دون انهيار التطبيق.
مثال بالكود
sealed interface NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>
data class Error(val code: Int, val message: String?) : NetworkResult<Nothing>
data class Failure(val throwable: Throwable) : NetworkResult<Nothing>
}
suspend fun <T> safeApiCall(apiCall: suspend () -> T): NetworkResult<T> {
return try {
NetworkResult.Success(apiCall())
} catch (e: HttpException) {
NetworkResult.Error(e.code(), e.message())
} catch (e: IOException) {
NetworkResult.Failure(e)
}
// CancellationException is NOT caught, so coroutine cancellation keeps working
}أخطاء شائعة
- التقاط Throwable بشكل أعمى بدون تمييز CancellationException، مما يمنع إلغاء الكوروتين عند مغادرة الشاشة.