Səhvlərin yayılması — səhvin yarandığı yerdən idarəediciyə qədər çağrılar yığınında yuxarıya doğru yayılma mexanizmi. Funksiya səhvi müstəqil idarə edə bilmədikdə, onu istisna (exception), throws deklarasiyası və ya qaytarılma növü vasitəsilə çağıran tərəfə ötürür. Səhvlərin düzgün yayılması mobil tətbiqlərin sabitliyi üçün vacibdir: işlənməmiş və ya səhv ötürülmüş səhvlər çökmələrə səbəb olur. Apple Swift Documentation (2026)-ya görə, Swift-də throws vasitəsilə avtomatik yayılma səhvi boilerplate kodu olmadan istənilən səviyyəyə ötürməyə imkan verir.
Əsas məqamlar
Səhvlərin yayılması — səhv obyektinin yarandığı funksiyadan çağrılar zənciri boyunca ən yaxın uyğun idarəediciyə qədər ötürülməsi prosesidir. Çağrılar yığınını təsəvvür edin: ViewController ViewModel-i, ViewModel Repository-ni, Repository API-ni çağırır. Əgər API şəbəkə xətası qaytarırsa, o, Repository və ViewModel vasitəsilə istifadəçiyə mesaj göstərəcək ViewController-a çatmalıdır. Hər bir aralıq funksiya qərar verir: səhvi idarə etmək və ya daha da ötürmək (yaymaq).
Yayılmanın iki yanaşması var: avtomatik və əl ilə. Avtomatik yanaşmada (Swift throws, Java yoxlanılmış istisnalar) kompilyator proqramçını ya səhvi idarə etməyə, ya da imzada yayılmanı bəyan etməyə məcbur edir. Əl ilə yanaşmada (Result Tipi, Kotlin Try) səhv dəyər kimi ötürülür — proqramçı səhvin ötürülməsi və ya transformasiyası üçün açıq kod yazır. Kotlin Result Docs (2026)-ya görə, Kotlin-də Result<T> birbaşa funksiya sərhədləri arasında yayılma üçün nəzərdə tutulmayıb — onu hər səviyyədə transformasiya etmək və ya idarə etmək lazımdır, bu da yayılmanı daha şüurlu, eyni zamanda daha uzun edir.
Yanaşma seçimi tətbiqin arxitekturasından və dildən asılıdır. Swift-də throws vasitəsilə avtomatik yayılma üstünlük təşkil edir, Kotlin-də — istisnaların (gözlənilməz səhvlər üçün) və Result tipli konteynerlərin (gözlənilən səhvlər üçün) qarışığı. Anlamaq vacibdir: yayılma məqsəd deyil, zərurətdir. İdeal arxitektura qərar qəbul etmək üçün kifayət qədər kontekstin olduğu ən aşağı səviyyədə səhvləri idarə edərək yayılma dərinliyini minimuma endirir.
Swift-də throws vasitəsilə yayılma avtomatik baş verir: əgər throws ilə A funksiyası throws ilə B funksiyasını çağırırsa və A do-catch-də B-nin səhvini idarə etmirsə, səhv avtomatik olaraq A-nı çağıran tərəfə ötürülür. Bu, Java yoxlanılmış istisnaları üçün xarakterik olan boilerplate kodunu aradan qaldırır, burada throws zəncirin hər metodunda bəyan edilməlidir. Swift prinsipi tətbiq edir: „zəncirdə bir throws funksiyası = aralıq səviyyələrdə idarə edilməzsə, bütün zəncir throws olur”.
struct UserRepository {
func fetchUser(id: Int) throws -> User {
let data = try networkService.request(path: "/users/\(id)")
return try parseUser(from: data)
}
}
class UserViewModel {
let repo = UserRepository()
func loadUser(id: Int) throws -> User {
return try repo.fetchUser(id: id)
}
}
// ViewController — son idarəedici
func onButtonTap() {
let vm = UserViewModel()
do {
let user = try vm.loadUser(id: 42)
updateUI(user)
} catch {
showError("İstifadəçi yüklənə bilmədi")
}
}
Yayılma zənciri: networkService.request -> fetchUser -> loadUser -> onButtonTap. Hər bir aralıq funksiya throws ilə işarələnib və do-catch ehtiva etmir — səhv avtomatik yuxarı ötürülür. ViewController onButtonTap do-catch ilə son idarəedicidir. Əgər ViewModel səhvi transformasiya etmək (başqa tipə bükmək) qərarı versəydi, do-catch və yeni throw istifadə edə bilərdi. Avtomatik yayılma kodu qısaldır: Repository səhvi necə idarə edəcəyini bilməli deyil — bu, istifadəçiyə mesaj göstərmək üçün UI-ya çıxışı olan ViewController-ın məsuliyyətidir.
Kotlin-də istisnalar vasitəsilə yayılma imzada throws bəyannaməsi tələb etmir (bütün istisnalar yoxlanılmamışdır). İstisna try-catch-ə rast gələnə qədər yığın boyunca avtomatik qalxır. Bununla belə, imzada throws-un olmaması yayılmanı qeyri-aşkar edir: proqramçı funksiyanın imzasından onun istisna ata biləcəyini görmür. Bu həm üstünlük (az boilerplate), həm də çatışmazlıqdır (idarə etməyi unutmaq asandır). Kotlin bu problemi dil vasitəsilə deyil, konvensiyalar və arxitektura nümunələri vasitəsilə həll edir.
class UserRepository(
private val api: ApiService,
private val db: Database
) {
suspend fun getUser(id: String): User {
return try {
api.fetchUser(id)
} catch (e: IOException) {
db.getCachedUser(id) ?: throw AppException("User unavailable")
}
}
}
class UserViewModel(private val repo: UserRepository) {
private val _state = MutableStateFlow<UiState<User>>(UiState.Loading)
val state: StateFlow<UiState<User>> = _state
fun loadUser(id: String) {
viewModelScope.launch {
try {
val user = repo.getUser(id)
_state.value = UiState.Success(user)
} catch (e: AppException) {
_state.value = UiState.Error(e.message ?: "Unknown")
}
}
}
}
Repository-də transformasiya ilə yayılma: IOException (şəbəkə əlçatan deyil) zamanı funksiya verilənlər bazasından keşlənmiş məlumatları almağa çalışır. Keş boşdursa, AppException atır — yayılma yeni səhv növü ilə davam edir. ViewModel AppException-ı tutur və UiState.Error-ə çevirir — səhv daha irəli getmir, yayılma UI qatında tamamlanır. Kotlin Coroutines xüsusiyyətlər əlavə edir: launch-dəki istisnalar avtomatik olaraq CoroutineExceptionHandler vasitəsilə yayılır, async-də isə yalnız await() çağırışında. SupervisorJob korutinalarda yayılmanı layihələndirərkən bunu nəzərə almaq vacibdir — SupervisorJob valideyn korutinanın ləğvinin qarşısını alır.
İstisnalara alternativ — uğur və ya səhvi dəyər kimi ötürən tip-konteyner vasitəsilə yayılma. Bu yanaşmada funksiya dəyəri deyil, bükümü qaytarır: Swift-də Result<T, E>, Kotlin-də Result<T>, Dart-da Either<L, R> (fpdart və ya dartz paketindən). Səhv yığını açmır — sadəcə konteynerdə yerləşir və növbəti səviyyə onunla nə edəcəyinə qərar verir. Bu, yayılmanı daha aşkar və idarə olunan edir.
data class HttpResult<out T>(
val data: T?,
val error: AppError?
) {
val isSuccess: Boolean get() = data != null
val isError: Boolean get() = error != null
}
sealed class AppError {
data class Network(val message: String) : AppError()
data class Auth(val message: String) : AppError()
}
fun fetchUser(id: String): HttpResult<User> {
return try {
val response = api.get("/users/$id")
HttpResult(data = parseUser(response), error = null)
} catch (e: IOException) {
HttpResult(data = null, error = AppError.Network("No internet"))
}
}
HttpResult<T> — data və error sahələri olan sadə konteyner. Sealed class AppError səhv növlərini (Network, Auth) müəyyən edir. fetchUser funksiyası HttpResult qaytarır, yayılma yığının açılmasını tələb etmir — çağıran tərəf sadəcə isSuccess/isError-ı yoxlayır. Bu yanaşma xüsusilə Təmiz Arxitekturada faydalıdır, burada hər bir qat (data, domain, presentation) səhvi transformasiya edə bilər: IOError -> DomainError -> UiError. Konteyner vasitəsilə yayılma bu transformasiyaları aşkar və test edilə bilən edir, istisnalardan fərqli olaraq, burada çevrilmə zənciri funksiya imzalarında görünmür.
Səhvlərin idarə edilməsinin layihələndirilməsində əsas qərarlardan biri yayılma (yuxarı ötürmək) və idarəetmə (burada idarə etmək) arasında seçimdir. Qərar qəbul etmə qaydası: səhvi mənalı hərəkət üçün kifayət qədər kontekstin olduğu səviyyədə idarə et. UI-ya girişin varsa — istifadəçiyə mesaj göstər. Keşə girişin varsa — bərpa etməyə çalış. Nə biri, nə də digəri yoxdursa — yay.
| Ssenari | Hərəkət | Əsaslandırma |
|---|---|---|
| Repository-də şəbəkə xətası | Yay | Repository istifadəçinin sorğunu təkrarlamaq istədiyini bilmir |
| Repository-də analiz xətası | İdarə et (standart dəyər qaytar) | Repository formatı bilir, ehtiyat dəyər qaytara bilər |
| ViewModel-də vaxt aşımı | İdarə et (UiState.Error) | ViewModel UiState-i idarə edir, səhvi necə çevirəcəyini bilir |
| Interceptor-da avtorizasiya xətası | İdarə et (tokeni yenilə) | Interceptor tokenlərə çıxışı var və sessiyanı bərpa edə bilər |
| UseCase-də naməlum xəta | Yay | UseCase-in UI konteksti yoxdur — yalnız biznes məntiqi |
Qızıl qayda: aşağı səviyyələrdə minimum yayılma, maksimum idarəetmə. Əgər Repository keşdən bərpa edə bilirsə — səhvi yuxarı ötürmədən bunu etməlidir. Əgər ViewModel Snackbar göstərə bilirsə — qoy göstərsin, ViewController-dan əlavə kod tələb etmədən. Hər bir yayılma səviyyəsi bağlılığı artırır və test etməyi çətinləşdirir. Google Android Architecture Guide (2026)-ya görə, ViewModel səviyyəsində bütün mümkün vəziyyətləri (Loading, Success, Error) təmsil etmək üçün sealed class UiState istifadə edərək qat sərhədləri arasında yayılmanı minimuma endirmək və istisnaları birbaşa UI qatına ötürməmək tövsiyə olunur.
Yanlış yayılma mobil tətbiqlərdə çətin aşkar edilən səhvlərin mənbəyidir. Proqramçıların qarşılaşdığı beş əsas problemi və onların həll yollarını nəzərdən keçirək.
Ən çox yayılmış problem: yayılma zamanı istisna tutulur, jurnala yazılır və orijinal istisna olmadan yenisi atılır. Proqramçı StackTrace-i itirir və səhvin harada baş verdiyini anlaya bilmir. Swift-də error chaining istifadə edin: throw MyError(context: originalError). Kotlin-də: throw AppException(cause = originalException). Dart-da: throw AppException(message, originalException). Heç vaxt səbəbi/əsas səhvi ötürmədən yeni istisna yaratmayın.
catch (e: Exception) { /* heç nə */ } — tətbiqin yanlış vəziyyətdə işləməyə davam etməsinə səbəb olan anti-nümunə. Səhvi görməməzliyə vura biləcəyinizə əminsinizsə — əsaslandırma ilə şərh əlavə edin. Swift-də seçmə görməməzliyə vurmaq üçün try? istifadə edin (səhv -> nil). Kotlin-də — Result<T>.onFailure { /* log */ }. Jurnala yazmadan istisnaları boğmayın.
Səhv idarə edilmədən 5+ səviyyədən keçirsə, arxitektura yenidən nəzərdən keçirilməlidir. Hər bir yayılma səviyyəsi aşağıdakı funksiyaların throws imzasından asılılıqdır. Həll: qat sərhədlərində Failure konteynerlərindən (sealed class Result { Success, Error }) istifadə edin ki, yayılma aşkar və məhdud olsun. Yayılma zənciri nə qədər qısa olsa, kodu test etmək və sazlamaq bir o qədər asandır.
Kotlin Coroutines-də launch-dəki istisna standart olaraq valideyn korutinanı və bütün siblings-i (eyni scope-un uşaqları) ləğv edir. 10 paralel tapşırıqdan biri düşərsə, qalan 9-u ləğv ediləcək, bu çox vaxt arzuolunmazdır. Səhvləri izolyasiya etmək üçün SupervisorJob və ya supervisorScope istifadə edin: bir uşaqdakı səhv siblings-i ləğv etmir. ViewModelScope standart olaraq SupervisorJob istifadə edir, bu da Android-də bu problemdən qoruyur.
Callback əsaslı API-də səhv tez-tez callback parametri kimi ötürülür. Əgər callback səhvi idarə etmirsə (və ya yanlış idarə edirsə), yayılma qeyri-aşkar olur və asanlıqla itir. Həll: async/await (Swift) və ya korutinalara (Kotlin) keçin, burada yayılma standart try-catch mexanizmləri vasitəsilə işləyir. Əgər callback qaçılmazdırsa — hər iki halın məcburi idarə edilməsi üçün Either<Error, T> və ya Result<T> istifadə edin.
Tez-tez verilən suallar
Throw — istisnanın bir dəfəlik atılması hərəkətidir. Səhvlərin yayılması — səhvin throw-dan catch-ə qədər bir neçə yığın səviyyəsi vasitəsilə ötürülməsinin bütün prosesidir. Yayılma throw-u, aralıq funksiyalar vasitəsilə avtomatik və ya əl ilə ötürməni və son idarəetməni əhatə edir. Bu, səhvin həyat dövrünü təsvir edən daha geniş anlayışdır.
Verilən ssenarilərdə istisnalar atan mock obyektlərindən istifadə edin. Funksiyanın səhvi düzgün yaydığını və ya idarə etdiyini assertThrows (Kotlin/JUnit) və ya XCTAssertThrowsError (Swift/XCTest) vasitəsilə yoxlayın. Result əsaslı yayılma üçün hər iki halda isSuccess/isError və dəyərləri yoxlayın.
Result yayılması bir arxitektura sərhədi daxilində gözlənilən səhvlər (yanlış məlumatlar, biznes qaydaları) üçün üstünlük təşkil edir. İstisnalar yüksək səviyyədə idarə edilməli olan gözlənilməz səhvlər (şəbəkə itkisi, giriş-çıxış səhvləri) üçün daha yaxşıdır. Səhv ilə nəticə icra axınını kəsmir, istisna isə kəsir.
Kotlin Coroutines-də launch-dəki istisna avtomatik olaraq siblings-in ləğvi ilə CoroutineScope vasitəsilə yayılır. İzolyasiya üçün supervisorScope və ya SupervisorJob istifadə edin: bir korutinadakı səhv digərlərini ləğv etmir. Async üçün səhv await() çağırışında try-catch vasitəsilə açıq şəkildə idarə edilməlidir, əks halda udulacaq.
İdeal son idarəedici UI qatıdır (ViewController, Fragment/Composable). Yalnız o, istifadəçi interfeysinə çıxışa malikdir və mesaj, Snackbar və ya dialoq göstərə bilər. Aralıq qatlar (Repository, UseCase, ViewModel) səhvi yayır, lazım gəldikdə onu daha abstrakt domen tipinə çevirir.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun