Swift 6 Strict Concurrency Rehberi: Sendable ve Veri Yarışı Güvenliği (2026)

Swift 6 strict concurrency rehberi: Sendable protokolü, aktör izolasyonu, region-based isolation ve veri yarışı hatalarını çözmenin pratik yolları.

Swift 6 Strict Concurrency Rehberi (2026)

Güncelleme: 18 Haziran 2026

Swift 6 strict concurrency, derleyicinin tüm veri yarışlarını derleme zamanında tespit edip hata olarak işaretlediği bir veri yarışı güvenliği modudur. Pratik olarak bu, paylaşılan mutable durumun yalnızca Sendable tipler aracılığıyla aktarılması, izole olmayan kapama bloklarının yasaklanması ve aktör sınırlarının derleyici tarafından zorlanması anlamına gelir. Bu rehberde Swift 6.2 (Xcode 26.3) ile birlikte gelen Sendable protokolünü, aktör izolasyonunu, bölge tabanlı izolasyonu ve mevcut bir projeyi complete concurrency moduna geçirmenin gerçek adımlarını anlatacağım. Geçen ay 120 bin satırlık bir bankacılık uygulamasını Swift 6'ya taşırken edindiğim notlar da burada.

  • Swift 6'da SWIFT_STRICT_CONCURRENCY=complete varsayılan olarak açıktır; uyarılar artık derleme hatasına dönüşür.
  • Sendable protokolü, bir değerin izolasyon sınırları arasında güvenle aktarılabileceğini derleyiciye bildiren bir işaret protokolüdür.
  • Region-based isolation (SE-0414) sayesinde Swift 6.2'de let olmayan bazı değerler bile artık aktarılabilir (transferring).
  • @MainActor, actor ve nonisolated kombinasyonu, UI ile arka plan iş yüklerini ayırmanın temel aracıdır.
  • @unchecked Sendable kullanımı son çare olmalıdır. Geliştirici, eşzamanlılık güvenliğinden manuel olarak sorumlu hale gelir.
  • Geçişi modül bazında incremental yapın: önce minimal, sonra targeted, en son complete.

Swift 6 strict concurrency nedir?

Strict concurrency, Swift derleyicisinin tüm potansiyel veri yarışlarını (data race) derleme zamanında saptayıp programın çalışmasını engellediği güvenlik modudur. Swift 5.x döneminde bu kontroller varsayılan olarak kapalıydı veya yalnızca uyarı üretiyordu. Swift 6 ile birlikte complete seviye standart hâle geldi ve uyarılar artık derleme hatası olarak rapor edilir. Apple bu adımı, modern paralel iOS, macOS ve visionOS uygulamalarında en yaygın crash kaynağı olan eşzamansız hataları ortadan kaldırmak için attı.

Pratik bir örnekle anlatalım. Aşağıdaki kod Swift 5'te uyarısız derlenirken, Swift 6'da hata verir çünkü counter birden fazla görev arasında izolasyonsuz olarak paylaşılıyor:

// Swift 6'da derleme hatası: "Mutation of captured var 'counter' in concurrently-executing code"
var counter = 0

await withTaskGroup(of: Void.self) { group in
    for _ in 0..<100 {
        group.addTask {
            counter += 1   // veri yarışı
        }
    }
}

Bu kontroller derleyici düzeyinde yapıldığı için runtime maliyeti yoktur. Yalnızca yazım anında daha fazla disiplin gerektirir; karşılığında uygulamanız klasik thread-safety bug'larından tamamen arınmış olur. Bu yaklaşım Swift resmi geçiş rehberinde ayrıntılı biçimde belgelenmiştir.

Sendable protokolü ve uyumluluk kuralları

Sendable, bir tipin değerlerinin eşzamanlılık sınırları (görevler, aktörler, izolasyon bölgeleri) arasında güvenle aktarılabileceğini ifade eden bir marker protocol'dür. İçinde hiçbir gereksinim yoktur; derleyici, tipin yapısına bakarak uygunluğu otomatik olarak doğrular veya reddeder. Anlamsal kural şudur: bir Sendable değerini bir görevden başka bir göreve aktardığınızda, iki taraf da onu güvenle okuyup yazabilmelidir.

Aşağıdaki tablo, hangi tiplerin otomatik olarak Sendable sayıldığını özetler:

Tip kategorisiOtomatik Sendable mi?Koşul
Değer tipleri (struct, enum)EvetTüm depolanan üyeleri Sendable ise
final classKoşulluTüm üyeleri immutable (let) ve Sendable ise
Aktör (actor)EvetHer zaman; izolasyon derleyici tarafından garanti edilir
Fonksiyon/closureHayır (varsayılan)@Sendable imzasıyla işaretlenmeli
Sınıf hiyerarşileri (non-final)HayırYalnızca @unchecked Sendable ile manuel onay
Generic tiplerKoşulluTip parametreleri Sendable ise (conditional conformance)

Pratik bir örnek. Basit bir DTO doğal olarak Sendable'dır:

struct UserProfile: Sendable {
    let id: UUID
    let displayName: String
    let createdAt: Date
}

// Doğrudan Task'lara aktarılabilir
Task {
    let profile = UserProfile(id: UUID(), displayName: "Ayşe", createdAt: .now)
    await uploadProfile(profile)   // güvenli
}

Aktör izolasyonu ve @MainActor

Aktörler (actor), mutable durumu kapsülleyip ona erişimi seri hâle getiren referans tipleridir. Bir aktörün üyelerine dışarıdan erişim her zaman await ile yapılır ve derleyici, aynı anda yalnızca bir görevin aktörün durumunu değiştirebilmesini garanti eder. Bu, kilitler (lock) veya kuyruklarla manuel senkronizasyona alternatif, derleyici tarafından doğrulanan bir modeldir.

actor ImageCache {
    private var storage: [URL: Data] = [:]

    func image(for url: URL) -> Data? {
        storage[url]
    }

    func insert(_ data: Data, for url: URL) {
        storage[url] = data
    }
}

// Kullanım
let cache = ImageCache()
let data = await cache.image(for: url)   // await zorunlu

@MainActor, özel ve tek bir global aktör'dür: kendisiyle işaretlenmiş tüm kod, ana iş parçacığında (main thread) çalışır. SwiftUI View tipleri ve UIKit UIViewController alt sınıfları Swift 6'da varsayılan olarak @MainActor'a izole edilmiştir. Arka planda çalışması gereken bir metodu tek tek dışarı almak için nonisolated kullanırsınız:

@MainActor
final class FeedViewModel: ObservableObject {
    @Published var items: [FeedItem] = []

    nonisolated func computeChecksum(of data: Data) -> String {
        // CPU yoğun, UI ile alakasız: arka planda çalışmasına izin ver
        SHA256.hash(data: data).description
    }

    func reload() async {
        let fresh = try? await FeedService.shared.fetch()
        items = fresh ?? []   // ana iş parçacığında
    }
}

SwiftUI tabanlı bir uygulamanın modern veri katmanını kurmak istiyorsanız, SwiftData ile @Model ve @Query kullanım rehberimiz aktör izolasyonu ile birlikte kullanılması gereken kalıcılık kalıplarını detaylandırır.

Region-based isolation (SE-0414)

Region-based isolation, Swift 6'nın en az anlaşılan ama en güçlü özelliğidir. Geleneksel kurala göre Sendable olmayan bir değer asla görev sınırlarını geçemezdi. SE-0414 önerisi ile derleyici artık değişkenlerin izolasyon bölgelerini analiz eder: bir nesne yalnızca tek bir referansla tutuluyorsa (kimse paylaşmıyorsa), Sendable olmasa bile başka bir aktöre veya göreve aktarılabilir (transferring).

// NSImage Sendable değil, ama burada izole bir bölgede üretildi
func processImage(at url: URL) async throws {
    let image = NSImage(contentsOf: url)!   // izole bölge
    // 'image' artık başka kimse tarafından tutulmuyor → aktarılabilir
    try await ImageProcessor.shared.process(image)   // Swift 6.2'de geçerli
}

Bu özellik, Sendable olmayan eski Foundation/AppKit tipleriyle çalışırken yaşanan ağrıyı büyük ölçüde azaltır. Derleyicinin "bu değer izole bir bölgede" çıkarımını yapabilmesi için değişkene başka referans tutmamanız gerekir. Değer aktarıldıktan sonra yerel kapsamda kullanmaya çalışırsanız hata alırsınız.

Sendable uyarılarını nasıl çözerim?

Sendable uyarıları ve hataları beş ana kategoriye düşer ve her birinin doğru çözüm yolu farklıdır. Yanlış çözüm, örneğin her şeye @unchecked Sendable yapıştırmak, yarışları gizler, çözmez. Aşağıda sık karşılaşılan durumlar ve önerilen çözümler yer alıyor.

1. "Capture of 'self' with non-sendable type in @Sendable closure"

Bir görev (Task) içinde bir sınıfın self'ini yakaladığınızda görülür. Sınıfı bir aktöre dönüştürün veya @MainActor'a izole edin:

@MainActor
final class DownloadManager {   // çözüm: izole et
    func start() {
        Task {
            await self.fetch()   // artık güvenli
        }
    }
}

2. "Type 'X' does not conform to the 'Sendable' protocol"

Sendable bir bağlama, Sendable olmayan bir değer geçirdiğinizde olur. Tip bir struct ise alanları kontrol edin; büyük olasılıkla içinde Sendable olmayan bir sınıf vardır. Mümkünse o sınıfı da değer tipine çevirin veya immutable bir final class yapın.

3. Closure'larda @Sendable eksikliği

Closure tipinin imzasına @Sendable ekleyin: (@Sendable () async -> Void).

4. Statik özelliklerde "shared mutable state"

Statik var kullanımları Swift 6'da tehlikeli kabul edilir. Çözüm: ya let yapın ya da bir aktörün arkasına yerleştirin.

5. NotificationCenter/observer kapama blokları

Observer closure'ları @Sendable olmak zorundadır. self'i [weak self] ile yakalayın ve içeride Task { @MainActor in ... } kullanın. Bu kalıbı bir push notification handler'da kaçırdığım için bir gece boyunca crash log okuduğumu hatırlıyorum, dikkatli olun.

@unchecked Sendable ne zaman kullanılır?

@unchecked Sendable, derleyiciye "ben bu tipin eşzamanlılık güvenliğini manuel olarak garanti ediyorum, kontrol etme" demenizi sağlar. Adından da anlaşılacağı üzere bu bir kaçış yoludur ve sorumluluğu sizden almaz; aksine size yükler. Yanlış kullanılan @unchecked Sendable, tam olarak Swift 6'nın engellemek için tasarlandığı veri yarışlarına yol açar.

Meşru kullanım senaryoları sınırlıdır:

  1. Kendi kilidini yöneten tipler: İçinde NSLock, os_unfair_lock veya benzeri ile senkronizasyon yapan eski sınıflar.
  2. Kopyalanmayan veri tutucu sınıflar (CoW): Yapı (struct) gibi davranan ama performans nedeniyle sınıf olarak yazılan tipler.
  3. Üçüncü taraf Sendable olmayan tipleri sarmalayan adaptörler: Apple framework'lerinin henüz güncellenmemiş tiplerini izole bağlamlarda kullanmak.
final class ThreadSafeCounter: @unchecked Sendable {
    private let lock = NSLock()
    private var _value = 0

    var value: Int {
        lock.lock(); defer { lock.unlock() }
        return _value
    }

    func increment() {
        lock.lock(); defer { lock.unlock() }
        _value += 1
    }
}

Swift 6 strict concurrency'ye geçiş adımları

Mevcut bir Swift 5 projesini Swift 6'ya geçirmek, hepsini bir gecede yapmaya çalışmak yerine kademeli yapıldığında çok daha az sancılıdır. Apple'ın resmi Swift 6 geçiş dökümantasyonu da modül başına ilerlemeyi önerir. Aşağıdaki sıralama, gerçek bir orta-büyük iOS projesinde uyguladığım çalışma akışıdır.

Adım 1: Swift 5 dilinde, strict concurrency'yi "minimal" açın

Xcode'da hedefin Build Settings → Swift Compiler - Upcoming Features altında SWIFT_STRICT_CONCURRENCY=minimal ayarlayın. Bu yalnızca açıkça Sendable işaretlenmiş tipleri kontrol eder. Çıkan uyarıları sıfıra indirin.

Adım 2: "targeted" seviyeye yükseltin

Bu seviye, Swift 6 ile uyumlu olarak imzalanmış API'lerde Sendable kontrolünü zorlar. Bu noktada framework'leri günceller ve kendi kodunuzun aktör izolasyonunu netleştirirsiniz.

Adım 3: Modül modülünde "complete"'a geçin

Tek bir hedefte SWIFT_STRICT_CONCURRENCY=complete deneyin. Hata sayısı yüzlerle ölçülebilir; panik yapmayın. Çoğu hata kalıbı tekrar eder; bir tipi izole edince ona bağımlı 30 hata birden çözülür. (Benim ilk denemem 412 hatayla başladı, ikinci günün sonunda 60'a düşmüştü.)

Adım 4: Swift Language Mode'u 6'ya alın

Tüm hedefler complete modda derlendiğinde SWIFT_VERSION=6 yapın. Bu noktada uyarılar derleme hatasına dönüşür ve geri dönüş yolu kapanır. Ama o kapı bir kere kapandığında, projenizde artık veri yarışı yoktur.

Geçiş sürecinde Xcode'un AI destekli düzeltme önerilerinden faydalanmak isterseniz Xcode 26.3 ajan tabanlı kodlama rehberimizdeki iş akışları, tekrarlayan Sendable düzeltmelerini önemli ölçüde hızlandırır.

Sık karşılaşılan hatalar ve çözümleri

Aşağıda strict concurrency geçişinde en çok karşılaştığım dört hata kalıbı ve önerilen düzeltmeler yer alıyor. Bu hatalar genellikle Swift 5'te sessizce çalışan koddan kaynaklanır; yani bug yoktur sanırsınız ama derleyici size doğru olanı söyler.

"Sending value of non-Sendable type"

Bir görev sınırı geçerken Sendable olmayan bir değer aktarıyorsunuz. İki yoldan biri: ya değeri Sendable yapın, ya da region-based isolation'a uygun şekilde yeniden yapılandırın (değerin başka referans tarafından tutulmadığından emin olun).

"Main actor-isolated property cannot be referenced from a non-isolated context"

Arka planda çalışan bir kapanış içinde MainActor'a izole bir özelliğe erişiyorsunuz. await MainActor.run { ... } bloğu ekleyin veya çağıran fonksiyonu @MainActor ile işaretleyin.

"Call to main actor-isolated initializer in a synchronous nonisolated context"

SwiftUI View initializer'larında oluşabilir. init'i @MainActor yapın veya ilgili tipi nonisolated init ile genişletin.

Singleton'lar ve global state

Klasik static let shared = MyService() kalıbı, MyService Sendable değilse derlemez. Çözüm: MyService'i bir actor'e dönüştürün veya @MainActor'a izole edin. Tüm singleton'ları aynı anda dönüştürmeye çalışmayın; kullanım sıklığına göre öncelik verin.

Sıkça Sorulan Sorular

Swift 6 strict concurrency'yi devre dışı bırakabilir miyim?

Evet, hedef bazında SWIFT_STRICT_CONCURRENCY=minimal ayarlayıp SWIFT_VERSION=5'te kalarak geçişi erteleyebilirsiniz, ancak iOS 18+ hedefleyen yeni projeler için bunu önermiyorum. Geçiş ne kadar geciktirilirse ileride yapılacak iş o kadar büyür.

actor ve @MainActor arasındaki fark nedir?

actor, kendi serileştirilmiş izolasyon bağlamına sahip yeni bir tip yaratır; her aktör örneği bağımsızdır. @MainActor ise tek ve global bir aktördür. Onunla işaretlenen her şey aynı paylaşılan ana iş parçacığında çalışır. UI kodu için @MainActor, kapsüllenmiş arka plan durumu için actor kullanın.

@unchecked Sendable kullanmak güvenli mi?

Yalnızca tipin thread-safety'sini manuel olarak (kilitler, atomik işlemler veya immutability ile) sağladığınızdan kesin eminseniz güvenlidir. Aksi takdirde, derleyicinin yakalamayı bıraktığı veri yarışlarını koda dahil etmiş olursunuz. Her kullanımı yorum satırıyla belgeleyin.

Swift 6 geçişi ne kadar sürer?

Orta büyüklükte (50 ila 150 bin satır) bir iOS projesinde tipik geçiş süresi, eşlik eden refactor yapılmadan 2 ila 4 hafta arasındadır. Asıl zaman alıcı kısım kodlama değil; doğru izolasyon stratejisine karar vermektir. Eski kilit tabanlı kodu doğrudan aktörlere çevirmek genellikle en temiz sonucu verir.

Region-based isolation hangi Xcode sürümünde geldi?

SE-0414 önerisi Swift 6.0 ile birlikte geldi ve Xcode 16.0'dan itibaren mevcuttur. transferring parametre anahtar sözcüğü gibi ilgili ergonomik iyileştirmeler Swift 6.2 / Xcode 26.3 ile tamamlanmıştır. Daha eski Xcode sürümlerinde aynı kalıpları yazmak çok daha fazla @unchecked Sendable gerektirir.

Editorial Team
Yazar Hakkında Editorial Team

Our team of expert writers and editors.