Error Propagation: nedir, hata yayılım mekanizması ve mobil geliştirmede nasıl çalışır

Yazar: IT Sectr Yayınlanma: 2026-05-26 Okuma süresi: 9 dk

Error Propagation, bir hatanın oluştuğu noktadan çağrı yığınında yukarı doğru işleyiciye yayılmasını sağlayan bir mekanizmadır. Bir işlev bir hatayı kendi başına işleyemediğinde, bir istisna (exception), throws bildirimi veya Return Type aracılığıyla çağıran tarafa iletir. Mobil uygulamaların kararlılığı için propagation'ın doğru uygulanması kritik öneme sahiptir: işlenmeyen veya yanlış iletilen hatalar çökmelere yol açar. Apple Swift Documentation (2026)'ya göre, Swift'te throws aracılığıyla otomatik yayılım, tekrar eden kod olmadan hatayı herhangi bir seviyeye iletmeyi sağlar.

Önemli Noktalar

  • Error Propagation — hatanın oluştuğu yerden yığında yukarı doğru bir işleyiciye iletilmesi, ara işlevleri atlayarak
  • Otomatik yayılım Swift'te throws aracılığıyla her yığın seviyesinde açık kod olmadan hatayı iletir
  • Manuel yayılım Kotlin ve Dart'ta her seviyede açık try-catch veya Result kabında iletme gerektirir
  • Kontrol edilen istisnalar Java'da imzada throws ile yayılımı zorunlu kılar, kontrol edilmeyenler yok sayılabilir
  • Result Type — yığın geri sarma olmadan hatanın bir değer olarak iletildiği istisnalara alternatif

Error Propagation Nedir?

Error Propagation (hata yayılımı), bir hata nesnesinin oluştuğu işlevden çağrı zincirinde yukarı doğru en yakın uygun işleyiciye iletilmesi sürecidir. Çağrı yığınını hayal edin: ViewController, ViewModel'i çağırır, ViewModel Repository'yi çağırır, Repository API'yi çağırır. API bir ağ hatası döndürürse, hata Repository ve ViewModel aracılığıyla ViewController'a gitmeli ve bu da kullanıcıya bir mesaj göstermelidir. Her ara işlev şuna karar verir: hatayı işlemek veya daha yukarı iletmek (yaymak).

Yayılım için iki yaklaşım vardır: otomatik ve manuel. Otomatik yaklaşımda (Swift throws, Java kontrol edilen istisnalar) derleyici, geliştiriciyi hatayı işlemeye veya imzada yayılımı bildirmeye zorlar. Manuel yaklaşımda (Result Type, Kotlin Try) hata bir değer olarak iletilir — geliştirici, hatayı iletmek veya dönüştürmek için açıkça kod yazar. Kotlin Result Docs (2026)'ya göre, Kotlin'de Result<T> doğrudan işlev sınırları arasında yayılım için tasarlanmamıştır — her seviyede dönüştürülmesi veya işlenmesi gerekir, bu da yayılımı daha bilinçli ancak daha ayrıntılı hale getirir.

Yaklaşım seçimi, uygulama mimarisine ve dile bağlıdır. Swift'te throws aracılığıyla otomatik yayılım baskındır, Kotlin'de istisnalar (beklenmeyen hatalar için) ve Result benzeri kaplar (beklenen hatalar için) karışımı kullanılır. Anlaşılması gereken önemli nokta: yayılım bir amaç değil, bir gerekliliktir. İdeal bir mimari, yayılım derinliğini en aza indirir ve bir karar vermek için yeterli bağlamın olduğu mümkün olan en düşük seviyede hataları işler.

Swift'te Throws ile Yayılım

Swift'te throws ile yayılım otomatiktir: throws ile A işlevi, throws ile B işlevini çağırırsa ve A, B'nin hatasını do-catch'te işlemezse, hata otomatik olarak A'nın çağrıcısına iletilir. Bu, zincirdeki her yöntemde throws bildirilmesi gereken Java kontrol edilen istisnalarının tipik tekrar eden kodunu ortadan kaldırır. Swift şu prensibi kullanır: «zincirde bir throws işlevi = tüm zincir throws olur (ara seviyelerde işlenmezse)».

swift
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 işleyici
func onButtonTap() {
    let vm = UserViewModel()
    do {
        let user = try vm.loadUser(id: 42)
        updateUI(user)
    } catch {
        showError("Kullanıcı yüklenemedi")
    }
}

Yayılım zinciri: networkService.request -> fetchUser -> loadUser -> onButtonTap. Her ara işlev throws ile işaretlenmiştir ve do-catch içermez — hata otomatik olarak yukarı iletilir. ViewController onButtonTap, do-catch ile son işleyicidir. ViewModel hatayı dönüştürmeye (başka bir türe sarmaya) karar verseydi, do-catch ve yeni bir throw kullanabilirdi. Otomatik yayılım kodu azaltır: Repository hatayı nasıl işleyeceğini bilmek zorunda değildir — bu, kullanıcıya mesaj göstermek için kullanıcı arayüzüne erişimi olan ViewController'ın sorumluluğudur.

Kotlin'de İstisnalar ile Yayılım

Kotlin'de istisnalar ile yayılım, imzada throws bildirimi gerektirmez (tüm istisnalar kontrol edilmez). İstisna, bir try-catch ile karşılaşana kadar otomatik olarak yığında yükselir. Ancak, imzada throws olmaması yayılımı örtük hale getirir: geliştirici, işlev imzasından bir istisna fırlatabileceğini göremez. Bu hem bir artı (daha az tekrar eden kod) hem de bir eksi (işlemeyi unutmak daha kolay) dir. Kotlin bu sorunu dilin kendisi aracılığıyla değil, kurallar ve mimari desenler aracılığıyla çözer.

kotlin
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'de, dönüşümle yayılım: IOException (ağ kullanılamıyor) durumunda işlev, veritabanından önbelleğe alınmış verileri almaya çalışır. Önbellek boşsa, AppException fırlatır — yayılım yeni bir hata türüyle devam eder. ViewModel, AppException'ı yakalar ve UiState.Error'a çevirir — hata daha ileri gitmez, yayılım UI katmanı seviyesinde sona erer. Kotlin Coroutines özellikler ekler: launch'taki istisnalar otomatik olarak CoroutineExceptionHandler aracılığıyla yayılır ve async'te — yalnızca await() çağrıldığında. Coroutine'lerde yayılım tasarlarken bu önemlidir — SupervisorJob, alt coroutine'deki bir hata durumunda üst coroutine'in iptalini önler.

Result Type ile Yayılım

İstisnalara bir alternatif, başarı veya hatayı bir değer olarak ileten bir kap türü aracılığıyla yayılımdır. Bu yaklaşımda işlev bir değer değil, bir sarıcı döndürür: Swift'te Result<T, E>, Kotlin'de Result<T>, Dart'ta Either<L, R> (fpdart veya dartz paketinden). Hata yığını geri sarmaz — sadece kabın içindedir ve sonraki seviye onunla ne yapacağına karar verir. Bu, yayılımı daha açık ve kontrol edilebilir hale getirir.

kotlin
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 ve error alanlarına sahip basit bir kaptır. Sealed class AppError, hata türlerini (Network, Auth) tanımlar. fetchUser işlevi HttpResult döndürür, yayılım yığın geri sarma gerektirmez — çağıran sadece isSuccess/isError'ı kontrol eder. Bu yaklaşım özellikle Clean Architecture'da kullanışlıdır, burada her katman (data, domain, presentation) hatayı dönüştürebilir: IOError -> DomainError -> UiError. Kap aracılığıyla yayılım, bu dönüşümleri açık ve test edilebilir hale getirir; dönüşüm zincirinin işlev imzalarında görünmediği istisnaların aksine.

Yayılım ve İşleme: Ne Zaman İletmeli, Ne Zaman İşlemeli

Hata işleme tasarımındaki en önemli kararlardan biri, yayılım (yukarı iletmek) ve işleme (burada işlemek) arasındaki seçimdir. Karar kuralı: anlamlı bir eylem için yeterli bağlamın olduğu seviyede hatayı işleyin. Kullanıcı arayüzüne erişiminiz varsa — kullanıcıya bir mesaj gösterin. Önbelleğe erişiminiz varsa — kurtarmayı deneyin. Hiçbiri yoksa — yayın.

SenaryoEylemGerekçe
Repository'de ağ hatasıYayRepository, kullanıcının isteği tekrarlamak isteyip istemediğini bilmez
Repository'de ayrıştırma hatasıİşle (varsayılanı döndür)Repository formatı bilir, yedek değer döndürebilir
ViewModel'de zaman aşımıİşle (UiState.Error)ViewModel UiState'i yönetir, hatayı nasıl çevireceğini bilir
Interceptor'da yetkilendirme hatasıİşle (token yenile)Interceptor token'lara erişebilir ve oturumu geri yükleyebilir
UseCase'de bilinmeyen hataYayUseCase'in UI bağlamı yoktur — yalnızca iş mantığı

Altın kural: minimum yayılım, alt seviyelerde maksimum işleme. Repository önbellekten kurtulabiliyorsa — hatayı yukarı iletmeden bunu yapmalıdır. ViewModel bir Snackbar gösterebiliyorsa — ViewController'dan ek kod gerektirmeden göstersin. Her yayılım seviyesi bağlılığı artırır ve testi karmaşıklaştırır. Google Android Architecture Guide (2026)'ya göre, ViewModel seviyesinde tüm olası durumları (Loading, Success, Error) temsil etmek için sealed class UiState kullanarak ve istisnaları doğrudan UI katmanına iletmeyerek katman sınırları arasında yayılımı en aza indirmek önerilir.

Error Propagation Sorunları ve Anti-örüntüleri

Yanlış yayılım, mobil uygulamalarda bulunması zor hataların kaynağıdır. Geliştiricilerin karşılaştığı beş ana sorunu ve bunları çözme yollarını inceleyelim.

Hata bağlamının kaybı

En yaygın sorun: yayılım sırasında bir istisna yakalanır, günlüğe kaydedilir ve orijinal istisna olmadan yenisi fırlatılır. Geliştirici StackTrace'i kaybeder ve hatanın tam olarak nerede oluştuğunu anlayamaz. Swift'te hata zincirleme kullanın: throw MyError(context: originalError). Kotlin'de: throw AppException(cause = originalException). Dart'ta: throw AppException(message, originalException). cause/underlyingError iletmeden asla yeni bir istisna oluşturmayın.

Hatayı yok sayma (boş catch)

catch (e: Exception) { /* hiçbir şey */ } bir anti-örüntüdür ve uygulamanın hatalı bir durumda çalışmaya devam etmesine neden olur. Hatayı yok sayabileceğinizden eminseniz — gerekçesiyle bir yorum ekleyin. Swift'te isteğe bağlı yok sayma için try? (hata -> nil) kullanın. Kotlin'de — Result<T>.onFailure { /* log */ }. Günlükleme olmadan istisnaları yutmayın.

Aşırı yayılım derinliği

Bir hata işlenmeden 5+ seviyeden geçiyorsa, mimari yeniden değerlendirilmelidir. Her yayılım seviyesi, alttaki işlevlerin throws imzasına bir bağımlılıktır. Çözüm: yayılımı açık ve sınırlı hale getirmek için katman sınırlarında Başarısızlık kapları (sealed class Result { Success, Error }) kullanın. Yayılım zinciri ne kadar kısa olursa, kodu test etmek ve hata ayıklamak o kadar kolay olur.

SupervisorJob olmadan coroutine'lerde yayılım

Kotlin Coroutines'te launch'taki bir istisna varsayılan olarak üst coroutine'i ve tüm siblings'i (aynı kapsamdaki çocuklar) iptal eder. 10 paralel görevden biri başarısız olursa, diğer 9'u iptal edilir ve bu genellikle istenmez. Hataları yalıtmak için SupervisorJob veya supervisorScope kullanın: bir çocuktaki hata siblings'i iptal etmez. ViewModelScope varsayılan olarak SupervisorJob kullanır ve bu, Android'de bu sorunu önler.

İşleme olmadan geri çağırma ile yayılım

Geri çağırma tabanlı API'lerde, hata genellikle geri çağırmanın bir parametresi olarak iletilir. Geri çağırma hatayı işlemezse (veya yanlış işlerse), yayılım örtük hale gelir ve kolayca kaybolur. Çözüm: yayılımın standart try-catch mekanizmaları aracılığıyla çalıştığı async/await (Swift) veya coroutine'lere (Kotlin) geçin. Geri çağırma kaçınılmazsa — her iki durumu da işlemeye zorlamak için Either<Error, T> veya Result<T> kullanın.

Sıkça Sorulan Sorular

Error Propagation, throw'dan nasıl farklıdır?

Throw, bir istisna fırlatmanın tek seferlik bir eylemidir. Error Propagation, throw'dan catch'e kadar birden çok yığın seviyesi aracılığıyla bir hatayı iletmenin tüm sürecidir. Yayılım, throw'u, ara işlevler aracılığıyla otomatik veya manuel iletmeyi ve son işlemeyi içerir. Bu, hatanın yaşam döngüsünü tanımlayan daha geniş bir kavramdır.

Error Propagation nasıl test edilir?

Belirli senaryolarda istisna fırlatan mock nesneler kullanın. İşlevin hatayı assertThrows (Kotlin/JUnit) veya XCTAssertThrowsError (Swift/XCTest) ile doğru şekilde yaydığını veya işlediğini kontrol edin. Result tabanlı yayılım için isSuccess/isError ve her iki durumdaki değerleri kontrol edin.

Result ile yayılım ne zaman istisnalardan daha iyidir?

Result yayılımı, bir mimari sınır içinde beklenen hatalar (geçersiz veri, iş kuralları) için tercih edilir. İstisnalar, yüksek seviyede işlenmesi gereken beklenmeyen hatalar (ağ kaybı, G/Ç hataları) için daha iyidir. Hatalı bir sonuç yürütme akışını kesintiye uğratmaz, istisna uğratır.

Kotlin coroutine'leri aracılığıyla bir hata nasıl yayılır?

Kotlin Coroutines'te launch'taki bir istisna, siblings'in iptali ile otomatik olarak CoroutineScope aracılığıyla yayılır. Yalıtım için supervisorScope veya SupervisorJob kullanın: bir coroutine'deki hata diğerlerini iptal etmez. Async için, await() çağrılırken try-catch ile hata açıkça işlenmelidir, aksi takdirde yutulur.

Hangi seviye son hata işleyicisi olmalıdır?

İdeal son işleyici UI katmanıdır (ViewController, Fragment/Composable). Yalnızca onun kullanıcı arayüzüne erişimi vardır ve bir mesaj, Snackbar veya iletişim kutusu gösterebilir. Ara katmanlar (Repository, UseCase, ViewModel) hatayı yayar ve gerekirse daha soyut bir alan türüne dönüştürür.

Özet

  • Error Propagation — istisnalar veya Result kapları aracılığıyla hatayı oluştuğu yerden yığında yukarı bir işleyiciye iletmek
  • Otomatik yayılım Swift'te throws aracılığıyla ara seviyelerde kod gerektirmez — hata kendi kendine yükselir
  • Manuel yayılım Kotlin'de açık try-catch ve her seviyede throw ile işlemeyi bilinçli ancak ayrıntılı yapar
  • Result Type yığın geri sarma olmadan hatayı bir değer olarak iletir, Clean Architecture'ta beklenen hatalar için uygundur
  • İşleme kuralı: bağlamı olan seviyede (UI) işle, bağlamı olmayan seviyeler (domain, data) aracılığıyla yay
  • Anti-örüntüler: boş catch, dönüşümde cause kaybı, aşırı yayılım derinliği, SupervisorJob'u yok sayma
  • Her katman için tür belirtilmiş hatalar (sealed class / enum) tasarlayın ve katman sınırlarını geçerken dönüştürün

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun