What are Actors in Swift?
Actors are a fundamental concurrency feature introduced in Swift 5.5 that provide a safe way to manage mutable state in concurrent programs. An actor is a reference type that protects its mutable state by ensuring that only one task can access its properties and methods at a time.
Think of an actor as a protective wrapper around data that automatically handles synchronization. When multiple tasks try to access an actor simultaneously, Swift ensures they wait in line, preventing data races and crashes that commonly occur in multi-threaded programming.
actor BankAccount {
private var balance: Double = 0.0
func deposit(_ amount: Double) {
balance += amount
}
func getBalance() -> Double {
return balance
}
}
The Problem Actors Solve
In traditional concurrent programming, when multiple threads access shared mutable data simultaneously, you risk:
Data corruption - partial writes creating invalid states
Crashes - accessing deallocated memory
Unpredictable behavior - race conditions producing different results
Actors eliminate these issues by creating an isolation boundary around their state.
How Actors Work
An actor processes one request at a time in a serialized manner. When you call an actor's method from outside its isolation context, you must use await, which creates a suspension point where your code waits for its turn to execute.
actor DownloadManager {
private var activeDownloads: [URL: Progress] = [:]
private var completedFiles: [URL: Data] = [:]
func startDownload(from url: URL) -> String {
if activeDownloads[url] != nil {
return "Download already in progress"
}
let progress = Progress()
activeDownloads[url] = progress
return "Download started"
}
func completeDownload(url: URL, data: Data) {
activeDownloads.removeValue(forKey: url)
completedFiles[url] = data
}
func getCompletedData(for url: URL) -> Data? {
return completedFiles[url]
}
}
// Usage requires await
func handleDownload() async {
let manager = DownloadManager()
let status = await manager.startDownload(from: URL(string: "https://example.com/file")!)
print(status)
}
For details about MainActor refer below article
https://dev.to/arshtechpro/understanding-mainactor-in-swift-when-and-how-to-use-it-4ii4
What are Global Actors?
Global actors extend the actor concept to provide app-wide synchronization domains. Unlike regular actors that protect individual instances, global actors can synchronize code across multiple types, ensuring all marked code executes on the same serial executor.
The Global Actor Protocol
To create a global actor, you implement a type with the @globalActor attribute and provide a shared instance:
@globalActor
actor NetworkActor {
static let shared = NetworkActor()
private init() {}
}
Why Use Global Actors?
Global actors excel when you need:
Cross-type synchronization - Multiple classes working with the same shared resource
Domain isolation - Keeping subsystems (networking, database, analytics) separate
Thread affinity - Ensuring certain code always runs on specific threads
Different Types of Actors in Swift
1. Instance Actors (Regular Actors)
Each instance maintains its own isolation domain. Perfect for protecting individual resources.
actor SessionManager {
private var sessions: [String: UserSession] = [:]
private var maxSessions = 5
func createSession(for userId: String) throws -> UserSession {
guard sessions.count < maxSessions else {
throw SessionError.limitReached
}
let session = UserSession(userId: userId)
sessions[userId] = session
return session
}
func invalidateSession(for userId: String) {
sessions.removeValue(forKey: userId)
}
func getActiveSessionCount() -> Int {
return sessions.count
}
}
2. Custom Global Actors
Create domain-specific global actors for different subsystems in your app:
// Analytics global actor
@globalActor
actor AnalyticsActor {
static let shared = AnalyticsActor()
private init() {}
}
@AnalyticsActor
class AnalyticsTracker {
private var eventCount = 0
func trackEvent(_ name: String) {
eventCount += 1
print("Event #\(eventCount): \(name)")
}
}
// Any function can join the analytics actor
@AnalyticsActor
func logUserAction(_ action: String) {
print("User action: \(action)")
}
// Usage
class ViewController: UIViewController {
func userTappedButton() async {
// Must use await when calling from outside the actor
await logUserAction("button_tap")
}
}
3. Reentrant Actors
Actors can call their own methods without deadlocking:
actor Calculator {
private var memory: Double = 0
func factorial(_ n: Int) -> Int {
if n <= 1 { return 1 }
return n * factorial(n - 1) // Reentrant call - no await needed
}
func storeInMemory(_ value: Double) {
memory = value
}
func recallMemory() -> Double {
return memory
}
}
4. Nonisolated Context
Parts of an actor that don't need synchronization can be marked nonisolated:
actor ConfigurationManager {
let appVersion = "1.0.0" // Immutable - safe to access
private var settings: [String: Any] = [:]
// Can be called without await
nonisolated func getAppVersion() -> String {
return appVersion
}
// Can access immutable data and perform calculations
nonisolated func calculateCacheKey(for endpoint: String) -> String {
return "\(appVersion)_\(endpoint)".replacingOccurrences(of: "/", with: "_")
}
// Requires await - accesses mutable state
func updateSetting(key: String, value: Any) {
settings[key] = value
}
}
Best Practices and Guidelines
1. Choose the Right Actor Type
- Use instance actors for protecting individual resources
- Use global actors for system-wide synchronization domains
- Use @MainActor specifically for UI updates (covered in previous article)
2. Minimize Actor Boundaries
Keep actor interfaces small and focused. Large actors with many methods increase contention.
3. Use Nonisolated Wisely
Mark computed properties and methods that don't access mutable state as nonisolated to improve performance.
4. Combine Actors Carefully
When multiple actors interact, design carefully to avoid deadlocks and ensure proper data flow.
Summary
By understanding actor types and their use cases, you can build robust, thread-safe iOS applications that handle concurrency elegantly and efficiently.
SOCIAL SHARE CARD GENERATOR