Global actors in Swift


Greetings, traveler!

Sometimes, you may need to execute a portion of your code within a specific actor, even if that code doesn’t belong to that actor. Here, the Global Actors are coming to the rescue.

MainActor

For example, you can use the @MainActor attribute to run your code on the main thread. Let’s delve into an example code that was written in the old way:

func doSmth() {
    guard Thread.isMainThread else {
        assert(false, "The function must be executed on the Main Thread.")
        return
    }
    
    print("Do something…")
}

Using the @MainActor attribute, we can guarantee that this function is always called on the main thread without excess code:

@MainActor
func doSmth() {
    print("Do something…")
}

Also, to run the code on the main thread, we can use the following maneuver:

func doSmth() async {
    await MainActor.run {
        print("Do something…")
    }
}

If we are working with a Task, then we can write such code:

func doSmth() {
    Task { @MainActor in
        print("Async method...")
    }
}

Can you guess what the difference will be if we write our function like this?

@MainActor
func doSmth() {
    Task {
        print("Async method...")
    }
}

In the first case, only the asynchronous request will run on the main thread, and in the second case, the entire function.

Custom Global Actors

You can also create your own global actors. But the essential condition is that such an actor must be a Singleton.

@globalActor
actor MyActor {
    static let shared = MyActor()
}

Here, you can read more about creating a Singleton with this approach.

By the way, you can read more about Singletons in my article, which is part of the series about Design Patterns.

Conclusion

This concludes our discussion of Global Actors. This tool is a convenient way to access Swift Concurrency features from anywhere. In the following article, we will explore the topic of sendables.