Swift uygulamanda hata mesajlarini ozenle hazirlayip, sonra bunlarin aslinda hic goruntulenmedigini fark ettin mi? Onun yerine kullanicilar (ya da debug sirasinda sen) soyle anlasilmaz mesajlar goruyor:
“The operation couldn’t be completed. (YourApp.YourError error 0.)”
Eger bu durumu yasadiysan, yalniz degilsin. Bu kafa karistirici davranis, dilin tanitilmasindan bu yana – yeni baslayanlardan uzmanlara kadar – Swift gelistiricilerini zorluyordu. Bugun bu durumun neden oldugunu aciklayacagim ve Swift hata yonetimini daha sezgisel hale getiren bir cozum sunacagim.
Swift’in Error Protokolunun Sasirtici Davranisi
Sorunu gosteren basit bir ornege bakalim:
enum NetworkError: Error {
case noConnectionToServer
case parsingFailed
var localizedDescription: String {
switch self {
case .noConnectionToServer:
return "No connection to the server."
case .parsingFailed:
return "Data parsing failed."
}
}
}
// Using the error
do {
throw NetworkError.noConnectionToServer
} catch {
print("Error message: \(error.localizedDescription)")
// Expected: "No connection to the server."
// Actual: "The operation couldn't be completed. (AppName.NetworkError error 0.)"
}Ne oldu? Net bir hata mesaji tanimladik ama Swift bunu tamamen gormezden geldi!
Neden Boyle Oluyor: NSError Koprusu
Bu kafa karistirici davranis, Swift’in Error protokolunun arka planda Objective-C’nin NSError sinifina koprulenmesindan kaynaklaniyor. localizedDescription‘a eristiginde Swift senin property’ni kullanmiyor – modul adini (domain), enum case’in tamsayi degerini (code) ve varsayilan bir mesaji iceren bir NSError olusturuyor.
Bu tasarim Objective-C uyumlulugu icin mantikli olabilir ama ozellikle Swift’e yeni baslayanlar icin berbat bir gelistirici deneyimi yaratiyor.
“Resmi” Cozum: LocalizedError
Swift aslinda resmi bir cozum sunuyor: LocalizedError protokolu. Iste nasil kullanman gerekiyor:
enum NetworkError: LocalizedError {
case noConnectionToServer
case parsingFailed
var errorDescription: String? { // Note: Optional String
switch self {
case .noConnectionToServer:
return "No connection to the server."
case .parsingFailed:
return "Data parsing failed."
}
}
// There are also these optional properties that are rarely used
var failureReason: String? { return nil }
var recoverySuggestion: String? { return nil }
var helpAnchor: String? { return nil }
}Bu ise yarasa da birkac problemi var:
Tum property’ler optional (
String?), yani bir case’i unutursan derleyici sana yardim etmezSadece
errorDescriptionlocalizedDescription‘i etkiler; diger property’ler genellikle gormezden gelinirIsimlendirme, hangi property’nin gorunen mesaji etkiledigini net olarak belirtmiyor
Hala Cocoa hata yonetim kaliplarina dayanan eski bir yaklasim kullaniyor
Daha Iyi Bir Cozum: Throwable Protokolu
Bu hayal kirikligini defalarca yasadiktan sonra, ErrorKit kapsaminda daha basit bir cozum olusturdum – Throwable adinda bir protokol:
public protocol Throwable: LocalizedError {
var userFriendlyMessage: String { get }
}Bu protokolun birkac avantaji var:
Tek, non-optional bir gereksinim var – artik case’leri unutma yok
userFriendlyMessageismi amaci acikca ifade ediyorUyumluluk icin
LocalizedError’u genisletiyor (senin icin ekstra is yok!)-ableson ekiyle Swift isimlendirme kurallarina uyuyor
Iste nasil kullaniyorsun:
enum NetworkError: Throwable {
case noConnectionToServer
case parsingFailed
var userFriendlyMessage: String {
switch self {
case .noConnectionToServer:
return "Unable to connect to the server."
case .parsingFailed:
return "Data parsing failed."
}
}
}
// Using the error
do {
throw NetworkError.noConnectionToServer
} catch {
print("Error message: \(error.localizedDescription)")
// Now correctly shows: "Unable to connect to the server." 🎉
}Throwable ile ne goruyorsan onu aliyorsun – hata mesajlarin hicbir surpriz olmadan tam istedigin gibi gorunuyor.
String Raw Value’larla Hizli Gelistirme
Hizli prototipleme icin Throwable string raw value’larla da sorunsuz calisiyor:
enum NetworkError: String, Throwable {
case noConnectionToServer = "Unable to connect to the server."
case parsingFailed = "Data parsing failed."
}
// That's it! No extra implementation neededRaw string degerleri otomatik olarak hata mesajlarin haline geliyor ve erken gelistirme asamasinda boilerplate kodunu ortadan kaldiriyor. Daha sonra duzgun lokalizasyon icin hazir oldugunuzda, userFriendlyMessage’in tam implementasyonunda String(localized:) kullanmaya gecebilirsin.
Kullanima Hazir Hata Turleri
Boilerplate kodu daha da azaltmak icin ErrorKit, yaygin senaryolar icin onceden tanimlanmis hata turleri iceriyor:
func fetchData() async throws {
guard isNetworkAvailable else {
throw NetworkError.noInternet
}
guard let url = URL(string: path) else {
throw ValidationError.invalidInput(field: "URL path")
}
// More implementation...
}Bu yerlesik turler sunlari iceriyor:
NetworkError– baglanti ve API sorunlari icinFileError– dosya sistemi islemleri icinDatabaseError– veri saklama sorunlari icinValidationError– girdi dogrulama icinPermissionError– yetkilendirme sorunlari icinVe daha bir cok tur…
Her yerlesik tur zaten Throwable’a uyumludur ve kutudan ciktiginda lokalize edilmis, kullanici dostu mesajlar sunar – zamandan tasarruf ederken netlik saglar.
GenericError ile Hizli Tek Seferlik Hatalar
Yeni bir hata turu tanimlamadan ozel bir mesaja ihtiyac duydugun durumlar icin ErrorKit GenericError sunuyor:
func quickOperation() throws {
guard condition else {
throw GenericError(userFriendlyMessage: "The operation couldn't be completed because a specific condition wasn't met.")
}
// More implementation...
}Bu, erken gelistirme veya ozel bir hata turu gerektirmeyen benzersiz hata durumlari icin mukemmel.
Daha Iyi Mesajlarin Otesindeki Faydalar
Throwable’i benimsemek sadece hata mesajlarini duzetmekle kalmiyor – birkac ek fayda da sagliyor:
Yeni gelisitriciler icin netlik: Protokol, hata mesajlarinin nasil tanimlanacagini acikca gosteriyor
Derleme zamani guvenligi: Non-optional gereksinim tum case’lerin mesajlarinin olmasini sagliyor
Lokalizasyon destegi: Uluslararasilastirma icin
String(localized:)ile mukemmel calisiyorAzaltilmis boilerplate: Ozellikle raw string degerler ve yerlesik turlerle
Gelistirilmis kullanici deneyimi: Net hata mesajlari, kullanicilarin neyin yanlis gittigini anlamasina yardimci oluyor
Daha iyi hata ayiklama: Anlamli hata mesajlari hata ayiklamayi hizlandiriyor
Gecis Yapmak
En guzel tarafi? Throwable, Error icin dogrudan bir yedek:
// Before
enum AppError: Error {
case configurationFailed
}
// After
enum AppError: Throwable {
case configurationFailed
var userFriendlyMessage: String {
switch self {
case .configurationFailed:
return "Failed to load configuration."
}
}
}throws, do/catch ve diger hata yonetim kaliplarini kullanan mevcut kod tamamen ayni sekilde calisiyor – tek fark artik hata mesajlarinin gercekten istedigin gibi gorunmesi.
Sonuc
Swift’in hata yonetimi guclu ama mesaj isleme kismi cok uzun suredir kafa karistirici bir sikinti noktasiydi. Throwable protokolu, Swift’in tasarim ilkeleriyle uyumlu, basit ve sezgisel bir cozum sunarken uzun suredir var olan bir sorunu duzeltiyor.
Hata turlerin icin Throwable’i benimseyerek daha net hata mesajlari, azaltilmis boilerplate ve daha sezgisel bir gelistirici deneyimi elde ediyorsun. Yerlesik hata turleri ve GenericError yedegi ile birlikte, bekledigin gibi calisan kapsamli bir hata yonetim yaklasimi olusturuyor.
Bu yaklasimi kendi projelerinde denemek istiyorsan, Throwable protokolunu, yerlesik hata turlerini ve Swift icin daha bircok hata yonetim iyilestirmesini iceren ErrorKit’e goz at:
Swift gelistirmende bu hata mesaji karisikligiyla karsilastin mi? Nasil cozum buldun? Sosyal medyada (asagidaki linklerden) bana bildir!

