Tuesday, April 23, 2024
HomeiOS DevelopmentDispatching to the Important thread with MainActor in Swift – Donny Wals

Dispatching to the Important thread with MainActor in Swift – Donny Wals


Printed on: April 23, 2024

Swift 5.5 launched a great deal of new concurrency associated options. One in all these options is the MainActor annotation that we are able to apply to courses, features, and properties.

On this publish you’ll study a number of methods that you should use to dispatch your code to the primary thread from inside Swift Concurrency’s duties or by making use of the primary actor annotation.

Should you’d prefer to take a deep dive into studying how one can work out whether or not your code runs on the primary actor I extremely suggest studying this publish which explores Swift Concurrency’s isolation options.

Alternatively, in case you’re thinking about a deep dive into Swift Concurrency and actors I extremely suggest that you just try my e-book on Swift Concurrency or that you just try my video course on Swift Concurrency. Each of those sources offers you deeper insights and background data on actors.

Dispatching to the primary thread by means of the MainActor annotation

The quickest option to get a perform to run on the primary thread in Swift Concurrency is to use the @MainActor annotation to it:

class HomePageViewModel: ObservableObject {
  @Printed var homePageData: HomePageData?

  @MainActor
  func loadHomePage() async throws {
    self.homePageData = attempt await networking.fetchHomePage()
  }
}

The code above will run your loadHomePage perform on the primary thread. The cool factor about that is that the await on this perform isn’t blocking the primary thread. As a substitute, it permits our perform to be suspended in order that the primary thread can do another work whereas we anticipate fetchHomePage() to return again with some information.

The impact that making use of @MainActor to this perform has is that the project of self.homePageData occurs on the primary thread which is nice as a result of it’s an @Printed property so we should always at all times assign to it from the primary thread to keep away from foremost thread associated warnings from SwiftUI at runtime.

Should you don’t like the concept of getting all of loadHomePage run on the primary actor, you may also annotate the homePageData property as a substitute:

class HomePageViewModel: ObservableObject {
  @MainActor @Printed var homePageData: HomePageData?

  func loadHomePage() async throws {
    self.homePageData = attempt await networking.fetchHomePage()
  }
}

Sadly, this code results in the next compiler error:

Important actor-isolated property ‘homePageData’ cannot be mutated from a non-isolated context

This tells us that we’re attempting to mutate a property, homePageData on the primary actor whereas our loadHomePage methodology isn’t operating on the primary actor which is information security drawback in Swift Concurrency; we should mutate the homePageData property from a context that’s remoted to the primary actor.

We will remedy this challenge in one among 3 ways:

  1. Apply an @MainActor annotation to each homePageData and loadHomePage
  2. Apply @MainActor to the complete HomePageViewModel to isolate each the homePageData property and the loadHomePage perform to the primary actor
  3. Use [MainActor.run](http://MainActor.run) or an unstructured job that’s remoted to the primary actor within loadHomePage.

The quickest repair is to annotate our whole class with @MainActor to run every thing that our view mannequin does on the primary actor:

@MainActor
class HomePageViewModel: ObservableObject {
  @Printed var homePageData: HomePageData?

  func loadHomePage() async throws {
    self.homePageData = attempt await networking.fetchHomePage()
  }
}

That is completely tremendous and can be sure that your whole view mannequin work is carried out on the primary actor. That is truly actually near how your view mannequin would work in case you didn’t use Swift Concurrency because you usually name all view mannequin strategies and properties from inside your view anyway.

Let’s see how we are able to leverage possibility three from the checklist above subsequent.

Dispatching to the primary thread with MainActor.run

Should you don’t need to annotate your whole view mannequin with the primary actor, you may isolate chunks of your code to the primary actor by calling the static run methodology on the MainActor object:

class HomePageViewModel: ObservableObject {
  @Printed var homePageData: HomePageData?

  func loadHomePage() async throws {
    let information = attempt await networking.fetchHomePage()
    await MainActor.run {
      self.homePageData = information
    }
  }
}

Notice that the closure that you just move to run isn’t marked as async. Which means any asynchronous work that you just need to do must occur earlier than your name to MainActor.run. The entire work that you just put within the closure that you just move to MainActor.run is executed on the primary thread which could be fairly handy in case you don’t need to annotate your whole loadHomePage methodology with @MainActor.

The final methodology to dispatch to foremost that I’d like to indicate is thru an unstructured job.

Isolating an unstructured job to the primary actor

in case you’re creating a brand new Process and also you need to be sure that your job runs on the primary actor, you may apply an @MainActor annotation to your job’s physique as follows:

class HomePageViewModel: ObservableObject {
  @Printed var homePageData: HomePageData?

  func loadHomePage() async throws {
    Process { @MainActor in
      self.homePageData = attempt await networking.fetchHomePage()
    }
  }
}

On this case, we should always have simply annotated our loadHomePage methodology with @MainActor as a result of we’re creating an unstructured job that we don’t want and we isolate our job to foremost.

Nevertheless, in case you’d have to put in writing loadHomePage as a non-async methodology creating a brand new main-actor remoted job could be fairly helpful.

In Abstract

On this publish you’ve seen a number of methods to dispatch your code to the primary actor utilizing @MainActor and MainActor.run. The primary actor is meant to substitute your calls to DispatchQueue.foremost.async and with this publish you may have all of the code examples you want to have the ability to do exactly that.

Notice that a few of the examples supplied on this publish produce warnings below strict concurrency checking. That’s as a result of the HomePageViewModel I’m utilizing on this publish isn’t Sendable. Making it conform to Sendable would do away with all warnings so it’s a good suggestion to brush up in your data of Sendability in case you’re eager on getting your codebase prepared for Swift 6.



Supply hyperlink

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments