MainActor — SwiftUI

Hasan Ali Şişeci
Appcent
Published in
4 min readMar 20, 2024

Herkese selamlar, bu yazıda Swift ve SwiftUI’da işlemlerimizi @MainActor ile nasıl main thread’de gerçekleştiririz buna değineceğiz.

Swift’in @MainActor’u tanıtması, eş zamanlılık yönetiminde önemli bir milestone’dur. Kullanıcı arayüzü işlemlerinin güvenli, sorunsuz ve anlaşılır olmasını sağlamak için sağlam ve sezgisel bir mekanizma sunar.

Swift 5.5 ve sonrasında, @MainActor özelliği async/await eşzamanlılık modelinde önemli bir rol oynamaktadır. Bu özellik sayesinde, kodunuzun belirli bölümlerini otomatik olarak main thread’de çalışacak şekilde ayarlayarak eşzamanlı programlamayı daha erişilebilir, güvenli ve hata yapma olasılığını azaltmak için tasarlanmıştır. UIKit ve SwiftUI, veri sorunlarını önlemek ve iyi bir UX deneyimi sağlamak için bu işlemlerin main thread’de yürütülmesini gerektirdiğinden, UI ile ilgili tüm işlemler için bu oldukça önemlidir.

Actor denilen yapılar Swift programlama dilindeki asenkron programlamada kullanılan bir yapıdır. Actor’lerle ilgili ayrıntılı bilgi edinmek isterseniz Emre’nin yazdığı “Understanding Swift Actors” makalesine göz atabilirsiniz.

Global actor’lerin ilk hedefleri, main thread’e odaklanan ve kullanıcı arayüzünü güncelleyen işlemleri yönetmektir. MainActor adı verilen bir global actor, main thread’i temsil eder ve kullanıcı arayüzünde hızlı değişiklikler yapmanın gerektiği durumlarda çok kullanışlıdır.

Main actor, Dispatch kütüphanesini kullanan sistemlerde temel concurrency uygulamasıdır ve özel bir yürütücü tarafından sarılan main dispatch kuyruğunu (DispatchQueue.main) temsil eder.

Main Thread’de Çalışmak

Main thread, UI ile ilgili tüm değişikliklerin gerçekleştirilmesi gereken özel bir thread olarak kabul edilir. Eğer bir uygulamada gerçekleştirilecek olan işlemler önemli bir zaman alacaksa, örneğin bir api’den response beklemek, bir dosyayı sıkıştırmak veya bir dosya indirme işlemi gerçektleştirmek vb., bu işlemi ayrı bir iş thread’de çalıştırmak istersiniz ve işlem tamamlandığında, kullanıcı arayüzünü güncellediğiniz main thread’e geri dönersiniz.

Xcode, eğer UI’da kullandığınız değişkenlerinizin değerlerini değiştirirken bunu main thread’de yaptığınızdan emin olamazsa size şu hatayı verir. Bu yüzden işlemlerimizi main thread’de yönettiğimizden emin olmalıyız.

Main thread'de bir işlem yapmak için klasikleşmiş bir yöntem olanDispatchQueue.main.async:

DispatchQueue.main.async {
// UI güncellemelerini burada yapabilirsiniz.
}

Fakat bunun yerine Swift concurrency kullanıyorsanız, diğer @MainActor closure’larını veya MainActor.run() yöntemini senkronize olarak kullanabilirsiniz. Bunun için 3 farklı yöntem tercih edebiliriz.

// (1)
Task.detached { @MainActor in
// UI güncellemelerini burada yapabilirsiniz.
}

// (2)
Task { @MainActor [weak self] in
// UI güncellemelerini burada yapabilirsiniz.
}

// (3)
Task {
await MainActor.run { [weak self] in
// UI güncellemelerini burada yapabilirsiniz.
}
}

Otomatik olarak Main Thread’de Çalışmak

@MainActor’ın asıl etkileyici özelliği, çoğu zaman bizim ekstra bir iş yapmamıza gerek kalmadan, fonksiyonları veya tüm değişkenleri otomatik olarak main actor üzerinde çalışacak şekilde ayarlamasıdır. Artık, manuel olarak main thread’e girip iş yapma ihtiyacını hatırlamak zorunda kalmıyoruz, çünkü derleyici bunu bizim için otomatik olarak yapıyor.

Aslında bir class, struct, fonksiyon veya değişken için @MainActor tanımlaması eklediğimizde, Swift derleyicisine “Bu kodun main thread’de çalıştığından emin ol!” demiş olursunuz. Bu özellik özellikle kullanıcı arayüzü güncellemeleri, olay işleme ve main thread’e bağlı API’lerle etkileşim için kullanışlıdır.

MainActor, özel bir attribute olarak kabul edilebilir ve property wrapper türlerine veya result builder türlerine benzer. Herhangi bir tanımlama, @MainActor kullanılarak ana aktörden izole olduğunu belirtebilir. Bu durumda normal actor izolasyon kısıtlamaları devreye girer: Main actor olarak tanımlanmış bir yapıya yalnızca başka bir main aktör senkronize olarak erişilebilir, bunun dışında erişebilmek için async olarak erişmemiz gerekir.

@Observable
class LoginViewModel {
@MainActor var showButton = false

@MainActor func showLoginButton() {
showButton = true
}

func hideLoginButton() {
Task {
await MainActor.run {
showButton = false
}
}
}

func printButtonValue() async {
print(showButton) // not marked with 'await' hatası
print(await showButton) // hata vermez
}
}

veya direkt olarak bir class’ı işaretleyebiliriz.

@MainActor
class NewWeatherViewModel: ObservableObject {
@Published var currentTemperature: Double = -5.2
@Published var city: String = "Sivas"
@Published var isSnowing: Bool = false
@Published var isRaining = false
}

Bir tanımlamayı @MainActor ile işaretleyerek, derleyiciye, bu işi main thread üzerinde çağrılmasını ve eş zamanlılık farkındalığı olan diğer kodların bu işlemi yapmasını zorunlu kılmasını belirtirsiniz. Ancak, eşzamanlılık farkındalığı olmayan birçok kod var ve @MainActor, bu durumlar için henüz bir şey yapmıyor, çünkü bunu yapmak, gerçekten güvenli olan durumlarda bile çok fazla uyarıya neden olabilir.

Eğer eski tip bir completion handler içinde @MainActor kodu çağırıyorsanız, şu anda main thread’e açıkça geri dönmeniz gerekmektedir. Mümkün olduğunda, completion handler’ları async fonksiyonlara dönüştürün veya completion handler’larınızda @Sendable veya @MainActor açıklamalarını ekleyebilirsiniz.

Tüm bunları özetlememiz gerekirse:

Kullanıcı arayüzünü güncelleme göreviyle sıkça anılsa da, @MainActor’un etkisi iOS uygulamalarının temel mimarisini uzanıyor. Bu özellik, daha öngörülebilir ve güvenli bir eşzamanlılık modeli sağlayarak, daha dayanıklı ve sürdürülebilir kodlar yazılmasını teşvik ediyor. MainActor’u kullanırken şu uygulamalara dikkat etmek önemlidir:

  • Seçici Kullanım: MainActor’u, kullanıcı arayüzü veya diğer main thread kullanan API’lerle etkileşim gerektiren görevler için kullanın.
  • Performans : Main thread yönetimini basitleştirirken, uygulama performansını göz önünde bulundurun ve gereksiz main thread çalışmalarından kaçının.
  • Diğer Eşzamanlılık Araçlarıyla Entegrasyon: @MainActor’u, Swift’in geniş eşzamanlılık özellikleriyle uyumlu olarak kullanarak kapsamlı ve güvenli uygulamalar oluşturun. MainActor’un iOS geliştirme sürecine getirdiği yenilik, geliştiricilere daha güvenli, daha temiz ve daha verimli kod yazma imkanı sunuyor. Bu özellik, eşzamanlılığın yönetilebilir ve sağlam uygulama mimarisi için temel olduğu yeni bir Swift geliştirme çağını başlatıyor.

Main actor hakkında anlatacaklarım bu kadardı. Başka bir yazıda görüşmek üzere! İyi kodlamalar!

--

--