• 29.08.2026 09:18:30
  • Admin Admin

SwiftUI ekranlarında actor, Sendable ve MainActor sınırlarını kurarak veri yarışlarını teşhis edin. Bu swift eğitimi rehberi, iOS MVVM katmanlarında eşzamanlı istek birleştirme, ölçüm ve release doğrulamasını ele alır.

SwiftUI Eğitiminde Swift Concurrency ile Veri Yarışlarını Önlemek

SwiftUI eğitimi için strict concurrency teşhisi

Bir ios eğitimi veya ios kursu kapsamında concurrency hatalarını yalnızca runtime crash olarak ele almak yetersizdir. Xcode'da uygulama target'ı için Build Settings altındaki Strict Concurrency Checking değerini Complete yapın ve aynı ayarı test target'ına da uygulayın. Bu ayar, actor-isolated bir property'ye farklı isolation bağlamından erişim, Sendable olmayan closure capture'ı ve global mutable state gibi noktaları derleme aşamasında tanılar. swift eğitimi ya da swift kursu projelerinde bu kontrolü yalnızca Debug'da açmak, Release derlemesinde farklı bir hata yüzeyi üretir.

xcodebuild   -scheme CatalogApp   -configuration Release   -destination 'generic/platform=iOS'   SWIFT_STRICT_CONCURRENCY=complete   build

xcode eğitimi sırasında sık görülen yanlış düzeltme, Foundation veya eski bir SDK sarmalayıcısındaki tüm uyarıları @preconcurrency import ile susturmaktır. Bu annotation, import edilen modülün concurrency annotation'larını daha gevşek yorumlatır; sizin kodunuzdaki gerçek isolation ihlalini çözmez. Bunun yerine sınırı tek bir adapter'da tutun, adapter dönüş tiplerini Sendable yapın ve Xcode Issue Navigator'da hata veren capture'ın hangi closure'a taşındığını izleyin. Örneğin bir delegate callback'i doğrudan view model'e vermek yerine callback verisini immutable bir DTO'ya kopyalamak, compiler'ın geçişi doğrulamasını sağlar.

Actor ile istek birleştirme ve Sendable veri sözleşmesi

Aynı SwiftUI ekranı birden fazla kez görünür olduğunda veya iki view aynı endpoint'i istediğinde, basit bir cache actor'ü tek başına yeterli olmayabilir. Actor, mutable cache sözlüğünü seri erişime alır; fakat await noktasında actor yeniden girişe açıktır. İlk çağrı ağ isteğinde beklerken ikinci çağrı cache'i boş görüp ikinci isteği başlatabilir. Aşağıdaki inFlight Task kaydı, tüm çağrıların aynı sonucu beklemesini sağlar. iphone uygulama geliştirme sırasında bu desen özellikle hızlı sekme geçişlerinde yinelenen API trafiğini ayıklamak için faydalıdır.

import Foundation

struct Product: Decodable, Identifiable, Sendable {
    let id: UUID
    let name: String
}

actor CatalogRepository {
    private let url = URL(string: "https://api.example.com/products")!
    private var cached: [Product]?
    private var inFlight: Task<[Product], Error>?

    func products() async throws -> [Product] {
        if let cached { return cached }
        if let inFlight { return try await inFlight.value }

        let requestURL = url
        let task = Task.detached(priority: .userInitiated) {
            let (data, response) = try await URLSession.shared.data(from: requestURL)
            guard let http = response as? HTTPURLResponse,
                  (200...299).contains(http.statusCode) else {
                throw URLError(.badServerResponse)
            }
            return try JSONDecoder().decode([Product].self, from: data)
        }

        inFlight = task
        defer { inFlight = nil }

        let value = try await task.value
        cached = value
        return value
    }
}

Buradaki kritik ayrıntı, Task.detached görevinin çağıranın cancellation durumunu otomatik devralmamasıdır. Tek bir görünümün iptal olması nedeniyle paylaşılan inFlight task'i iptal ederseniz, aynı sonucu bekleyen diğer ekranları da başarısız yaparsınız. İptal politikasını bilinçli seçin: tek tüketicili indirmede task handle'ını iptal edin; çok tüketicili repository'de ise referans sayacı veya kısa bir TTL cache kullanın. Bu actor'ü Thread Sanitizer ile doğrulamak için Scheme - Diagnostics - Thread Sanitizer seçeneğini açın; actor dışına taşmış mutable state varsa test akışında raporlanır.

iOS MVVM mimarisi içinde MainActor sınırını daraltmak

ios mvvm mimarisi içinde view model'in tamamını @MainActor yapmak, UI state yazımlarını güvenli hale getirir; repository'yi MainActor'a taşımak ise JSON decode ve ağ yanıtının UI executor'ünde sıraya girmesine neden olabilir. UI state ile I/O katmanını ayırın. Aşağıdaki örnekte CatalogRepository kendi actor'ünde çalışır, view model ise yalnızca state geçişlerini ana actor üzerinde yapar.

import Observation

enum CatalogState {
    case idle
    case loading
    case loaded([Product])
    case failed(String)
}

@MainActor
@Observable
final class CatalogViewModel {
    private(set) var state: CatalogState = .idle
    private let repository: CatalogRepository

    init(repository: CatalogRepository) {
        self.repository = repository
    }

    func load() {
        guard case .loading = state else { return }
        state = .loading

        Task { [repository] in
            do {
                let products = try await repository.products()
                state = .loaded(products)
            } catch is CancellationError {
                state = .idle
            } catch {
                state = .failed(error.localizedDescription)
            }
        }
    }
}

View tarafında çağrıyı .task(id:) ile bağlayın; böylece kimlik değiştiğinde SwiftUI önceki görevi iptal edebilir. Ancak iptal, zaten tamamlanmış bir isteğin sonucunun state'e yazılmayacağını tek başına garanti etmez; özellikle detached veya callback tabanlı adapter'larda sonuç geç gelebilir. Ekran kimliğini view model'de saklayıp sonuç uygulamadan önce eşleştirmek, hesap değiştirme gibi durumlarda eski hesabın ürünlerinin yeni ekranda görünmesini engeller. Bu sınırlandırma, swiftui eğitimi örneklerinde sık atlanan ancak production akışında görülen stale-result hatasını hedefler.

Swift Concurrency değişikliğini Instruments ile ölçmek

Actor eklemek veya detached task kullanmak doğru olduğu varsayılan bir optimizasyon değildir; decode işlemi küçükse task oluşturma ve context switch maliyeti ölçülebilir. Yük senaryosunun başını ve sonunu OSSignposter ile işaretleyin. Instruments'ta Points of Interest ve Time Profiler trace'lerini aynı kayıtta açarak, işaretli interval sırasında main thread örneklerini ve decode fonksiyonunun self time değerini karşılaştırın.

import os

private let signposter = OSSignposter(
    subsystem: "com.example.catalog",
    category: "catalog-load"
)

func measuredLoad() async throws -> [Product] {
    let interval = signposter.beginInterval("catalog_load")
    defer { signposter.endInterval("catalog_load", interval) }
    return try await repository.products()
}

Önce actor ve inFlight kaydı olmayan branch'te temiz uygulama verisiyle 30 yükleme çalıştırın; ardından aynı cihaz, aynı endpoint fixture'ı ve aynı ağ koşuluyla yeni branch'i çalıştırın. Her iki trace için p50 ve p95 interval süresini, HTTP isteği sayısını ve main thread'deki decode stack sample yüzdesini kaydedin. Instruments'ın Swift Concurrency görünümünde aynı anda kaç task'in beklediğini inceleyin. İstek sayısı düşerken p95 yükseliyorsa, actor içinde yanlışlıkla CPU-ağır decode veya image transform çalıştırıp tüm çağrıları tek executor kuyruğuna dizmiş olabilirsiniz.

App Store yayınlama öncesinde concurrency kapısını kurmak

app store yayınlama öncesinde yalnızca uygulama target'ını derlemek yeterli değildir; widget, share extension ve test bundle'ları kendi Swift compiler ayarlarını taşıyabilir. CI job'ında önce target ayarlarını görünür kılın, sonra Release archive üretin. Bu komut, strict concurrency ayarının archive konfigürasyonunda gerçekten devrede olup olmadığını log'a taşır.

xcodebuild -scheme CatalogApp -configuration Release -showBuildSettings   | grep -E 'SWIFT_STRICT_CONCURRENCY|SWIFT_VERSION'

xcodebuild archive   -scheme CatalogApp   -configuration Release   -destination 'generic/platform=iOS'   -archivePath "$PWD/build/CatalogApp.xcarchive'

CI'da warning'leri hata yapmak istiyorsanız bunu önce ayrı bir doğrulama job'ında SWIFT_TREAT_WARNINGS_AS_ERRORS=YES ile deneyin. Üçüncü taraf binary framework uyarıları nedeniyle doğrudan release pipeline'ını kilitlemek yerine, yeni concurrency uyarılarının sayısını baseline ile karşılaştıran bir job daha güvenlidir. Ayrıca archive komutundaki scheme'in extension target'larını gerçekten içerdiğini Xcode Organizer içindeki archive ürünlerinden doğrulayın; ana uygulamada MainActor ihlali yokken extension'da eski delegate kodunun yarış üretmesi sık rastlanan bir release açığıdır.

Sık Sorulan Sorular

swift eğitimi sırasında Sendable hatası için @unchecked Sendable kullanmalı mıyım?

Yalnızca tipin mutable state'inin tüm erişim yollarını siz kontrol ediyorsanız kullanın. Örneğin immutable value type için Sendable eklemek yeterlidir; reference type için actor, lock veya serial executor kanıtı olmadan @unchecked Sendable yazmak compiler kontrolünü devre dışı bırakır. Önce Xcode'un hata mesajındaki closure capture'ını immutable DTO'ya dönüştürmeyi deneyin.

ios mvvm mimarisi içinde repository neden @MainActor olmamalı?

Repository'nin network sonrası yaptığı JSON decode, disk okuma veya cache dönüşümü MainActor üzerinde kalırsa render işlemleriyle aynı executor kuyruğunu paylaşır. Repository'yi actor yapın, yalnızca @MainActor view model'de state atayın ve Time Profiler ile decode stack'inin main thread sample oranını önce-sonra karşılaştırın.

swiftui eğitimi projelerinde .task iptali ağ isteğini mutlaka durdurur mu?

Hayır. .task görünüm kaybolunca görevi iptal eder, ancak detached task veya callback tabanlı API bu cancellation sinyalini devralmayabilir. Tek tüketicili isteklerde Task handle saklayıp cancel edin; paylaşılan actor içi inFlight istekte ise bir tüketicinin iptalinin diğer tüketicileri bozmayacağı bir referans sayacı ya da TTL stratejisi uygulayın.

iphone uygulama geliştirme CI pipeline'ında strict concurrency nasıl doğrulanır?

Release konfigürasyonunda xcodebuild ile SWIFT_STRICT_CONCURRENCY=complete kullanarak build alın, -showBuildSettings çıktısını CI artifact'i olarak saklayın ve uygulama dışındaki extension target'larını da archive içinde kontrol edin. Ardından Thread Sanitizer açık test scheme'i ile paylaşılan mutable state içeren entegrasyon testlerini çalıştırın.

AI / LLM Discovery

Bu makale Opendart Akademi iOS / Swift eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt