Instruction

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the previous lesson, you learned about asynchronous programming in Swift; by using the async and await keywords, the code becomes simple to write and easy to read.

Now, it’s time to consider other scenarios, such as shared state and race conditions.

Shared State

Shared state adheres to the SOLID Single Responsibility Principle. This principle explains that classes should have only one job.

Race Conditions

Stateful representation of a feature should be in one place, which means that many different objects can access the data.

Actors

Enter: Actors! Swift actors address many of the common race condition challenges by:

What Are Actors?

An actor in Swift is a reference type that provides thread-safe access to its mutable state. Unlike classes and structs, actors automatically serialize access to their data. This prevents simultaneous modifications from multiple threads.

Heimaga Yvmorsx Lkolmuw Ivyifp Vowpopjekdz Bojhhacv Vab fougg-ef Keg waiws-es Deuzk-uc lufiifowovoem Wibozirtu bw. Renea Nalau gmfu Junuwejju ypzu Rezijuxpu zfwe Sdpaum Hefaww Xamoot (Ajgufarja miwo) Honiic (Mpkhdgudozejaah) Uejuyamal (Tipaupefut)

Understanding the actor Keyword

To create a Swift actor, use the actor keyword:

actor BankAccount {
    private var balance: Double = 0.0

    func deposit(amount: Double) {
        balance += amount
    }

    func getBalance() -> Double {
        balance
    }
}

let account = BankAccount()

Task {
    await account.deposit(amount: 100.0)
    let balance = await account.getBalance()
    print("Current balance: \(balance)")
}

Understanding @MainActor

The Swift Concurrency framework provides a special kind of actor, called MainActor. MainActor is global, and only one instance of it exists. It guarantees that tasks are run on the main thread, which is essential for updating UI components.

@MainActor
class UserInterface {
    func updateLabel(with text: String) {
        print("Updating label with: \(text)")
    }
}

let ui = UserInterface()

Task {
    await ui.updateLabel(with: "Hello, World!")
}
Task { @MainActor in
    await someAsyncTask()
}

Trying out @MainActor

Let’s put @MainActor to use and see how it interacts with async functions.

import Foundation

// 1
actor AsyncActor {
    // 2
    func doSomethingAsync() async -> String {
        print("doSomethingAsync() is running on", Thread.current) // ignore the error for test purposes
        return "good bye"
    }
}

// 3
class MainClass {
    // 4
    private var asyncActor = AsyncActor()

    func main() {
        // 5
        Task { @MainActor in
            // 6
            print("main is running on", Thread.current) // ignore the error for test purposes
            let _ = await asyncActor.doSomethingAsync()
        }
    }
}

// 7
let mainClass = MainClass()
mainClass.main()

Playground Console Output Icon
Jzowsviejv Xajrezu Iexkic Ipop

Main Actor Playground
Coiw Oyvih Wzoxlqaamw

main is running on <_NSMainThread: 0x60000170c000>{number = 1, name = main}
doSomethingAsync() is running on <NSThread: 0x600001707540>{number = 3, name = (null)}

Update the Weather App

Launch the WeatherSampleApp project in Xcode and open the WeatherRepositoryImpl.swift file. Find the following line:

class WeatherRepositoryImpl: WeatherRepository {
actor WeatherRepositoryImpl: WeatherRepository {
class HomeViewModel: ObservableObject {
  @Published var state: HomeState = .empty

  private let weatherRepo: WeatherRepository

  init(weatherRepo: WeatherRepository = WeatherRepositoryImpl()) {
    self.weatherRepo = weatherRepo
  }

  // 1
  func getWeather(query: String) {
    state = .loading

    // 2
    Task { @MainActor in
      do {
        let weatherData = try await weatherRepo.fetchWeather(for: query)
        // 3
        state = .ready(weatherData)
      } catch (_) {
        // 4
        state = .error
      }
    }
  }
}
See forum comments
Download course materials from Github
Previous: Introduction Next: Conclusion