İçeriğe geç / Skip to content / Zum Inhalt

Swift'e Giriş: Optionals, Guard Let ve If Let Kullanımı

Ahmet Balaman

Son güncelleme:

5 dk okuma

SwiftiOSOptionalsGuard LetSwiftUI
Swift'e Giriş: Optionals, Guard Let ve If Let Kullanımı

Swift öğrenmeye başlayan herkes ilk hafta aynı duvara çarpar: Optionals. Derleyici bir değeri kullanmanıza izin vermez, ekran Value of optional type 'String?' must be unwrapped hatasıyla dolar ve en kısa çıkış yolu gibi görünen ! işareti her yere serpiştirilir. Birkaç hafta sonra da uygulama beklenmedik yerlerde çöker.

Bu yazıda Optional'ı bir engel olarak değil, Swift'in en güçlü güvenlik aracı olarak ele alıyoruz. Flutter ve Dart'tan gelenler için iyi bir haber var: kavramların çoğu tanıdık. Farklı olan birkaç noktayı özellikle işaretleyeceğim. Örnekler güncel Swift sürümleriyle (Swift 6 dahil) derlenir.

Optional Nedir?

Swift'te bir değişken ya bir değer tutar ya da hiçbir şey. "Hiçbir şey" durumu nil ile ifade edilir, ama bir değişkenin nil olabilmesi için tipinin Optional olarak işaretlenmesi gerekir. Apple'ın Optional belgesi bunu "bir değeri saran, ya o değeri ya da hiçbir şeyi tutan tip" diye tanımlar.

var name: String = "Ahmet"   // nil olamaz
var surname: String? = nil   // Optional: nil olabilir
surname = "Balaman"

String? aslında Optional<String>'in kısaltmasıdır ve Optional, iki durumu olan bir enum'dur: .none (değer yok) ve .some(değer). Bu ayrıntı ileride switch ile Optional'ları ele alırken işe yarayacak.

Optional'lar yalnızca sizin tanımladığınız değişkenlerde karşınıza çıkmaz. Standart kütüphanede "başarısız olabilecek" her işlem Optional döndürür:

let age = Int("42")          // Int?  (metin sayı değilse nil)
let first = [3, 5, 8].first  // Int?  (dizi boşsa nil)
let capitals = ["TR": "Ankara", "DE": "Berlin"]
let capital = capitals["FR"] // String?  (anahtar yoksa nil)

Int("42") ifadesi Dart'taki int.tryParse'ın karşılığıdır: dönüşüm başarısızsa hata fırlatmaz, nil döndürür.

Dart'tan Gelenler İçin Önemli Fark

Dart'ta bir değişkeni null kontrolünden geçirdiğinizde derleyici tipi kendiliğinden daraltır. Swift bunu yapmaz:

let email: String? = "[email protected]"

if email != nil {
    // print(email.count) // Hata: email hâlâ String?
}

Swift'te değeri kullanmak için onu açıkça "açmanız" (unwrap) gerekir. Aşağıdaki yöntemlerin hepsi bu işe yarar.

Optional Açma Yöntemleri

Optional çözümleme akışı: değer ya da nil sorusu, if let ve guard let ile güvenli kullanım, nil durumunda else dalı

1. Force Unwrapping (!)

En kısa ama en tehlikeli yöntemdir. Değer nil ise uygulama çalışma zamanında çöker:

let age: Int? = 25
print(age!)  // 25; age nil olsaydı uygulama çökerdi

Derslerde en sık düzelttiğim alışkanlık budur. ! yazmak, derleyiciye "bu asla nil olmaz, sorumluluk bende" demektir. Bu sözü gerçekten verebildiğiniz durumlar azdır: örneğin projeye eklediğiniz ve var olduğunu bildiğiniz bir kaynak dosya. Kullanıcıdan, ağdan ya da diskten gelen hiçbir veride ! kullanmayın.

2. if let: Değer Varsa Kullan

let email: String? = "[email protected]"

if let email {
    print("E-posta: \(email)") // Burada email artık String
} else {
    print("E-posta bulunamadı")
}

if let email { yazımı Swift 5.7 ile geldi ve if let email = email ifadesinin kısaltmasıdır: blok içinde aynı isimle, açılmış bir kopya oluşturur. Eski eğitimlerde sık görülen if let userEmail = email gibi yeni isimler uydurmaya artık gerek yok.

Birden fazla değeri ve ek koşulları tek satırda açabilirsiniz:

let username: String? = "ahmet"
let age: Int? = 25

if let username, let age, age >= 18 {
    print("\(username) yetişkin bir kullanıcı")
}

Virgülle ayrılmış koşulların hepsi sağlanırsa blok çalışır. Bu yazım, iç içe if let blokları kurmaktan (öğrencilerin "piramit" dediği yapıdan) çok daha okunaklıdır.

3. guard let: Değer Yoksa Erken Çık

func printUserInfo(name: String?, age: Int?) {
    guard let name, let age else {
        print("İsim ve yaş gerekli")
        return
    }

    // Buradan sonra name ve age açılmış olarak kullanılabilir
    print("\(name) - \(age) yaşında")
}

printUserInfo(name: "Ahmet", age: 25) // Ahmet - 25 yaşında
printUserInfo(name: nil, age: 25)     // İsim ve yaş gerekli

guard'ın else bloğu kapsamdan çıkmak zorundadır (return, throw, break ya da continue); derleyici bunu kontrol eder. Karşılığında, açılan değerler guard satırından sonra fonksiyonun geri kalanında kullanılabilir. Bu kalıbı en çok async/await ile ağ isteklerinde kullanacaksınız; sunucudan gelen verinin eksik alanları guard let ile tek tek elenir.

if let mi, guard let mi?

İkisi aynı işi yapıyor gibi görünse de kodun şeklini farklı yönlendirir.

if let değer olsa da olmasa da işin devam ettiği durumlar içindir. Değer varsa bir şey, yoksa başka bir şey yapılır:

let discounts: [String: Double] = ["YAZ10": 10, "OGRENCI": 25]

func finalPrice(total: Double, couponCode: String?) -> Double {
    if let couponCode, let discount = discounts[couponCode] {
        return total - discount
    }
    return total // Kupon yoksa ya da geçersizse normal fiyat
}

print(finalPrice(total: 100, couponCode: "YAZ10")) // 90.0
print(finalPrice(total: 100, couponCode: nil))     // 100.0

guard let fonksiyonun devam edebilmesi için değerin şart olduğu durumlar içindir. Önce ön koşullar kontrol edilir, asıl iş en sonda girintisiz olarak yazılır:

func charge(card: String, amount: Double) {
    print("\(card) kartından \(amount) TL çekildi")
}

func processPayment(amount: Double?, cardNumber: String?) {
    guard let amount, amount > 0 else {
        print("Geçerli bir ödeme tutarı gerekli")
        return
    }

    guard let cardNumber, !cardNumber.isEmpty else {
        print("Kart numarası gerekli")
        return
    }

    charge(card: cardNumber, amount: amount)
}

Pratik bir kural: bir fonksiyonun başında birden fazla if let iç içe geçmeye başladıysa, o kod neredeyse her zaman guard let ile daha okunaklı hâle gelir. Kodun "mutlu yolu" (her şeyin yolunda gittiği senaryo) sola yaslanmış ve kesintisiz kalır.

Nil-Coalescing Operatörü (??)

Değer nil ise yerine kullanılacak varsayılanı belirtir. Dart'taki ?? ile aynıdır:

let userName: String? = nil
let displayName = userName ?? "Misafir"

print(displayName) // Misafir

Arayüzde çok kullanılır: kullanıcı adı yoksa "Misafir", profil fotoğrafı yoksa varsayılan görsel gibi. Dictionary için ayrıca default: parametresi vardır: stock["elma", default: 0] anahtar yoksa 0 döndürür. Optional bir değerin ekrana nasıl bağlandığını SwiftUI'da @State ve @Binding ile veri akışı yazısında görebilirsiniz.

Optional Chaining

İç içe Optional'lar üzerinden güvenle ilerlemek için ?. kullanılır:

struct Address {
    var city: String?
}

struct Profile {
    var address: Address?
}

struct User {
    var profile: Profile?
}

let user = User(profile: Profile(address: nil))
let cityName = user.profile?.address?.city ?? "Bilinmiyor"
print(cityName) // Bilinmiyor

Zincirin herhangi bir halkası nil ise ifadenin tamamı nil olur ve ?? devreye girer. Dart'taki ?. ile aynı mantıktır. Bu yazının eski sürümünde bu modeller class olarak yazılmıştı; Swift'te veri modelleri için genellikle struct tercih edilir. Nedenini struct ve class farkı yazısında anlattım.

Zincirleme yalnızca özelliklerle değil, metotlarla da çalışır: user.profile?.address?.city?.uppercased() ya bir String ya da nil üretir.

switch ile Optional

Optional bir enum olduğu için switch ile de ele alınabilir. Değerin olmadığı durumu ayrı, olduğu durumu ayrı ele almak istediğinizde okunaklı bir yoldur:

let score: Int? = 85

switch score {
case nil:
    print("Henüz not girilmedi")
case let value? where value >= 50:
    print("Geçti: \(value)")
case let value?:
    print("Kaldı: \(value)")
}

case let value? kalıbı, değeri açarak eşleştirir; where ile ek koşul eklenebilir.

Sık Yapılan Hatalar

  • Derleyici hatasını susturmak için ! eklemek; hata ortadan kalkmaz, çalışma zamanına ertelenir.
  • if x != nil kontrolünden sonra x! yazmak; bunun yerine if let x kullanın.
  • İç içe if let blokları kurmak; virgülle birleştirin ya da guard let kullanın.
  • if let ile değeri açıp her seferinde yeni bir isim uydurmak; kısa if let x { yazımı yeterli.
  • Veri modellerinde her alanı Optional yapmak; bir değer her zaman varsa Optional olmamalı, yoksa kodun her yerinde açma işlemi gerekir.

Sonraki Adım

Optional'lar oturduktan sonra doğal devam, arayüz tarafıdır: SwiftUI ile ilk uygulama. Bu temelleri bir eğitmenle çalışmak isterseniz en iyi Swift öğretmeni nasıl bulunur yazısı seçim ölçütlerini anlatıyor.

Kaynaklar

Sık Sorulan Sorular

if let email { } şeklindeki kısa yazım nedir?

Swift 5.7 ile gelen kısaltma: if let email = email yerine if let email { yazabilirsiniz; aynı isimle unwrap edilmiş bir kopya oluşturur. guard let email else { return } için de geçerlidir.

String! gibi implicitly unwrapped optional ne zaman kullanılır?

Değerin nesne oluşturulduktan hemen sonra kesinlikle dolacağını bildiğiniz durumlarda, klasik örneği UIKit'teki @IBOutlet alanlarıdır. SwiftUI ile yazılan kodda neredeyse hiç ihtiyaç duymazsınız; şüphedeyseniz normal ? kullanın.

if x != nil ile if let arasında fark var mı?

if x != nil yalnızca değerin var olup olmadığını kontrol eder, x blok içinde hâlâ Optional'dır ve kullanmak için yine unwrap gerekir. Değeri kullanacaksanız if let ya da guard let tercih edin.

Swift'teki Optional, Dart'taki nullable tiplerle aynı mı?

Fikir aynıdır: ? ile işaretlenmeyen tip nil (Dart'ta null) olamaz, ?? ve ?. iki dilde de vardır. En önemli fark, Dart'ın null kontrolünden sonra değişkeni kendiliğinden daraltması, Swift'in ise değeri if let ya da guard let ile açıkça açmanızı istemesidir.

Yorumlar