Swift 6 nihayet en cok talep edilen ozelliklerden birini Swift’e getirdi: Typed Throws. Bu iyilestirme, bir fonksiyonun tam olarak hangi hata turlerini firlatabilecegini belirtmeni sagliyor ve Swift’in tur guvenligini hata yonetimine tasiyor. Ancak bu gucle birlikte “ic ice gecme cehennemi” diyebilecegim yeni bir zorluk geliyor – uygulamanin katmanlari arasinda hatalarin nasil yayildigini etkileyen bir sorun.
Bu yazida ic ice gecme sorununu aciklayacak ve bunu ErrorKit icerisinde boilerplate olmadan typed throws’u pratikte kullanilabilir kilan basit bir protokolle nasil cozdugumuzu gosterecegim. Bonus olarak, duzgun hata zincirlemenin hata ayiklama deneyimini nasil onemli olcude iyilestirebilecegini goreceksin.
Typed Throws: Vaad ve Sorun
Once Swift 6’da Typed Throws’un bize ne sagladigina bakalim:
// Instead of just 'throws', we can specify the error type
func processFile() throws(FileError) {
if !fileExists {
throw FileError.fileNotFound(fileName: "config.json")
}
// Implementation...
}Bu, cagirma noktasinda daha iyi hata yonetimi sagliyor:
do {
try processFile()
} catch FileError.fileNotFound(let fileName) {
print("Could not find file: \(fileName)")
} catch FileError.readFailed {
print("Could not read file")
}
// No generic catch needed if we've handled all possible FileError cases!Faydalari acik:
Hata yonetiminin derleme zamani dogrulamasi
Catch bloklarinda
as?ile tur donusumu gerektirmiyorCagiranlara tam olarak neyin yanlis gidebilecegini soyleyen, kendi kendini belgeleyen API
Hata case’leri icin IDE otomatik tamamlama
Ic Ice Gecme Cehennemi Sorunu
Sorun cok katmanli uygulamalarla calisirken ortaya cikiyor. Suna bak:
// Database layer throws DatabaseError
func fetchUser(id: String) throws(DatabaseError) {
// Database operations...
}
// Profile layer needs to call the database layer
func loadUserProfile(id: String) throws(ProfileError) {
do {
// ⚠️ Problem: This throws DatabaseError, not ProfileError
let user = try fetchUser(id: id)
} catch {
// Manual error conversion needed
switch error {
case DatabaseError.recordNotFound:
throw ProfileError.userNotFound
default:
throw ProfileError.databaseError(error) // Need a wrapper case
}
}
}Bu birkac sorun yaratiyor:
Wrapper Case Patlamasi: Her hata turunun tum olasi alt hatalar icin wrapper case’lere ihtiyaci var
Manuel Hata Eslemesi: Acik hata donusumu iceren tekrarlayan do-catch bloklari
Tur Cogalmasi: Hata turleri her katmanda buyuyor, yonetimi zorlastiyor
Kayip Baglam: Orijinal hata hakkindaki detaylar ceviride genellikle kayboluyor
Kucuk uygulamalar icin bu yonetilebilir olabilir. Cok katmanli buyuk uygulamalar icin ise hizla “ic ice gecme cehennemi” haline geliyor.
Cozum: Catching Protokolu
ErrorKit bunu Catching adinda basit bir protokolle cozuyor:
public protocol Catching {
static func caught(_ error: Error) -> Self
}Bu protokol, herhangi bir hatayi senin turune saran caught adinda tek bir enum case gerektiriyor. Iste nasil kullaniyorsun:
enum ProfileError: Throwable, Catching {
case userNotFound
case invalidProfile
case caught(Error) // Single case for all other errors
var userFriendlyMessage: String {
switch self {
case .userNotFound:
return "User not found."
case .invalidProfile:
return "Profile data is invalid."
case .caught(let error):
// Use the wrapped error's message
return ErrorKit.userFriendlyMessage(for: error)
}
}
}Throwable’in Error icin dogrudan bir yedek oldugunu unutma (bkz. onceki yazi).
Simdi sihir, protokolle birlikte gelen catch fonksiyonuyla gerceklesiyor:
func loadUserProfile(id: String) throws(ProfileError) {
// For known errors, throw them directly
guard isValidID(id) else {
throw ProfileError.invalidInput
}
// For operations that may throw other error types, use the catch function
let user = try ProfileError.catch {
// Any error thrown here will be automatically wrapped
// into ProfileError.caught(error)
return try fetchUser(id: id)
}
// Rest of implementation...
}catch fonksiyonunun closure’da ne donersen onu dondurdugunu unutma.
catch fonksiyonu closure’inda firlatan hatalari otomatik olarak senin hata turune sariyor. Manuel do-catch bloklari yok, acik hata eslemesi yok – sadece calisiyor. Birden fazla try ifadesi bile mumkun.
catch Fonksiyonunun Gizli Sosu
catch fonksiyonu zarif bir sekilde basit:
extension Catching {
public static func `catch`<ReturnType>(
_ operation: () throws -> ReturnType
) throws(Self) -> ReturnType {
do {
return try operation()
} catch {
throw Self.caught(error)
}
}
}Bu fonksiyon:
Hata firlatabilen bir closure aliyor
Calistirmayi deniyor
Basarili olursa sonucu donduruyor
Firlanan herhangi bir hatayi senin
caughtcase’ini kullanarak otomatik sariyorIslemin donus turunu koruyuyor
En guzel tarafi? Swift 6’nin typed throws’uyla sorunsuz calisiyor, boilerplate’i ortadan kaldirirken tur guvenligini koruyor.
Hata Ayiklama Icin Hata Zincirini Korumak
Bu yaklasimin en buyuk faydalarindan biri tam hata zincirini korumasi. Hatalar sinirlari gecerken baglami kaybetmek yerine, her katman orijinal hatayi korurken bilgi ekliyor.
ErrorKit, guclu hata ayiklama icin errorChainDescription(for:) fonksiyonuyla bundan yararlaniyor:
do {
try await updateUserProfile()
} catch {
print(ErrorKit.errorChainDescription(for: error))
// Output shows the complete chain:
// AppError
// └─ ProfileError
// └─ DatabaseError
// └─ FileError.notFound(path: "/Users/data.db")
// └─ userFriendlyMessage: "Could not find database file."
}Bu hiyerarsik gorunum sunu soyluyor:
Hatanin nereden kaynaklandigini (FileError)
Uygulamandan gectigi tam yolu (FileError -> DatabaseError -> ProfileError -> AppError)
Neyin yanlis gittiginin spesifik detaylarini (dosya bulunamadi, yol ile birlikte)
Kullanicilara gosterilecek kullanici dostu mesaji
Bu seviyede bir icgoru, ozellikle hatalarin cagri yigininin derinlerinden kaynaklanabilecegi karmasik uygulamalar icin hata ayiklama sirasinda cok degerli.
Yapilandirilmis Hata Zinciri Ciktisi
Hata zinciri tanimi, hata yapisini tekrarlamali olarak inceleyerek calisiyor:
static func errorChainDescription(for error: Error) -> String {
// Recursive implementation that builds a hierarchical description
Self.chainDescription(for: error, enclosingType: type(of: error))
}chainDescription’in ErrorKit icindeki tam implementasyonu icin buraya bak.
Fonksiyon, Swift’in yansima yeteneklerini su sekilde kullaniyor:
Mirror API kullanarak hatayi inceliyor
Catching’e uyumlu hatalar icin sarilan hatayi cikartiyorEnum hatalari icin case adlarini ve iliskili degerleri yakaliyor
Struct veya class hatalari icin tur meta verisini dahil ediyor
Her seyi hiyerarsik bir agac yapisinda biclendiriyor
Bu, ozellikle karmasik hata hiyerarsileri icin standart hata loglamasindan cok daha fazla bilgi sagliyor.
ErrorKit’te Yerlesik Destek
ErrorKit’in tum yerlesik hata turleri (ornegin FileError veya NetworkError) zaten Catching’e uyumlu, yani hemen kullanabilirsin:
func saveUserData() throws(DatabaseError) {
// Automatically wraps SQLite errors, file system errors, etc.
try DatabaseError.catch {
try database.beginTransaction()
try database.execute(query)
try database.commit()
}
}Gercek Dunya Ornegi: Tipik Bir Uygulama
Bunun daha kapsamli bir ornekte nasil calistigina bakalim:
// Data Access Layer
func fetchUserData(id: String) throws(DatabaseError) {
guard database.isConnected else {
throw DatabaseError.connectionFailed
}
// This could throw file system errors
try DatabaseError.catch {
let query = try QueryBuilder.build(for: id)
return try database.execute(query)
}
}
// Business Logic Layer
func processUserProfile(id: String) throws(ProfileError) {
guard isValidID(id) else {
throw ProfileError.invalidInput
}
// This automatically wraps DatabaseError
let userData = try ProfileError.catch {
return try fetchUserData(id: id)
}
// Process the user data...
}
// Presentation Layer
func displayUserProfile(id: String) throws(UIError) {
// This automatically wraps ProfileError (which might contain DatabaseError)
let profile = try UIError.catch {
return try processUserProfile(id: id)
}
// Display the profile...
}Bir veritabani baglantisi basarisiz olursa, hata zincirinde sunu goreceksin:
UIError
└─ ProfileError
└─ DatabaseError.connectionFailed
└─ userFriendlyMessage: "Unable to establish a connection to the database. Check your network settings and try again."Bu sana tam olarak ne oldugunu ve hatanin nereden kaynaklandigini soyluyor, hata ayiklamayi cok daha kolaylastiriyor. Eklenen baglam sorunu cozmek icin dogru ipucunu verebilir!
Sonuc
Swift 6’nin typed throws’u dile guclu bir eklenti ama katmanlar arasi hata yayilimi icin zorluklar getiriyor. Catching protokolu, boilerplate’i ortadan kaldirirken tur guvenligini koruyan basit ve zarif bir cozum sunuyor.
ErrorKit’in errorChainDescription fonksiyonuyla birlestirince, hata yonetimi guclu bir hata ayiklama aracina donusuyor. ErrorKit’i simdi kullan ve gercek dunya uygulamalarinda Swift’te hata yonetimini daha kullanisli hale getiren bircok baska iyilestirmeden faydalan:
Swift 6’nin typed throws’unu kullanmaya basladin mi? Uygulamalarinda katmanlar arasi hata yayilimini nasil yonetiyorsun? Sosyal medyada (asagidaki linklerden) bana bildir!

