Modern Concurrency: Beyond the Basics

Oct 20 2022 · Swift 5.5, iOS 15, Xcode 13.4

Part 2: Concurrent Code

16. GlobalActor

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 15. Writing Safe Concurrent Code With Actors Next episode: 17. Creating a GlobalActor

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 16. GlobalActor

Throughout this course, whenever you’ve needed to make quick changes that drive the UI, you’ve called MainActor.run(...) to access MainActor from anywhere. Because your app runs on a single main thread, you can’t create a second or a third MainActor. So it does make sense that there’s a default, shared instance of that actor that you can safely use from anywhere.

There are other times you need an app-wide, single-instance shared state, for example:

  • The app’s database layer is usually a singleton type that manages the state of a file on disk.
  • Image or data caches are also often single-instance types.
  • The authentication status of the user is valid app-wide, whether they have logged in or not.

Luckily, Swift allows you to create your own global actors, just like MainActor, for situations where you need a single, shared actor that’s accessible from anywhere.

@globalActor actor ImageDatabase {
  static let shared = ImageDatabase()
  // ...
}

Annotating an actor with the @globalActor attribute makes it automatically conform to the GlobalActor protocol. Its only requirement is a static property called shared to expose an actor instance that you make globally accessible. You don’t need to inject the actor from one type to another, or into the SwiftUI environment.

So how do you write code in your app, to work with singletons like databases or persistent caches?

@ImageDatabase func say(_ text: String) {
  // ... automatically runs on ImageDatabase ...
}

To avoid data races, any method that accesses your global actor needs to run on it: You just annotate the method with the global actor.

@ImageDatabase class DiskStorage { 
  // ...
}

You can annotate a complete class with a global actor, and that will add that actor’s semantics to all its methods and properties (as long as they aren’t nonisolated).

You can use the @ annotation to group methods or entire types that can safely share mutable state in their own synchronized silo.

In the next episode, you’ll create a global actor you can use as a persistent image cache layer.