İçeriğe geç

Swift'te Hata Yonetimini Dogru Yapmak: Objective-C Hata Mirasindan Kurtulmak

'(YourError error 0)' gibi anlasilmaz Swift hata mesajlariyla mi karsilasiyorsun? Iste bunlari kalici olarak duzeltmenin yolu -- net ve zarif bir sekilde.

Swift'te Hata Yonetimini Dogru Yapmak: Objective-C Hata Mirasindan Kurtulmak

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 etmez

  • Sadece errorDescription localizedDescription‘i etkiler; diger property’ler genellikle gormezden gelinir

  • Isimlendirme, 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

  • userFriendlyMessage ismi amaci acikca ifade ediyor

  • Uyumluluk icin LocalizedError’u genisletiyor (senin icin ekstra is yok!)

  • -able son 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 needed

Raw 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 icin

  • FileError – dosya sistemi islemleri icin

  • DatabaseError – veri saklama sorunlari icin

  • ValidationError – girdi dogrulama icin

  • PermissionError – yetkilendirme sorunlari icin

  • Ve 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:

  1. Yeni gelisitriciler icin netlik: Protokol, hata mesajlarinin nasil tanimlanacagini acikca gosteriyor

  2. Derleme zamani guvenligi: Non-optional gereksinim tum case’lerin mesajlarinin olmasini sagliyor

  3. Lokalizasyon destegi: Uluslararasilastirma icin String(localized:) ile mukemmel calisiyor

  4. Azaltilmis boilerplate: Ozellikle raw string degerler ve yerlesik turlerle

  5. Gelistirilmis kullanici deneyimi: Net hata mesajlari, kullanicilarin neyin yanlis gittigini anlamasina yardimci oluyor

  6. 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:

github.comFlineDev / ErrorKitSimplified error handling with built-in user-friendly messages for common errors. Fully localized. Community-driven

Swift gelistirmende bu hata mesaji karisikligiyla karsilastin mi? Nasil cozum buldun? Sosyal medyada (asagidaki linklerden) bana bildir!

Bu serideki sonraki yazilar:

  1. Unlocking the Power of Swift 6’s Typed Throws with Error Chains

  2. Better Error Reporting in Swift Apps: Automatic Logs + Analytics

  3. Making Swift Error Messages Human-Friendly—Together

Bu yazıyı beğendin mi? Swift ipuçları ve indie geliştirici güncellemeleri için Bluesky ve Mastodon üzerinden takip et.