A simple guide to Swift actors

An overview of the actor type declaration from Swift 5.5 and how it works with Swift concurrency


What is an Actor?

Starting with Swift 5.5, an actor is a new way to declare a type, just like you would with class, enum or struct. Unlike these other classes, however, the actor type declaration provides thread-safe access to mutable properties and functions with Swift’s concurrency system via async / await.

Actors vs Classes

Actors are very similar to classes (they are passed by reference) but come with one key difference that allow them to ideally operate in a concurrent environment.

  • Actors (automatically!) allow only one caller to access their mutable properties and methods at any given time.

For example, let’s say we want some data store that will hold all of our posts for our blogging app. Rather than create a class, we can create an actor that will allow for multithreaded access to our posts data.

actor PostsDataStore {
    var posts = Set<Post>()

    func add(_ post: Post) {
        posts.insert(post)
    }

    func remove(_ post: Post) {
        posts.remove(post)
    }
}

By using the actor type declaration, all of PostsDataStore’s methods & properties are now implicitly thread-safe. Now let’s look at how we can interact with these methods & properties using async / await.

Using Actor methods and properties with async / await

In order to call PostsDataStore’s fetchAll method, the PostsViewModel has to use the await keyword in an asynchronous context. Under the hood, this ensures that the operations of adding and fetching are serialized correctly between threads.

class PostsViewModel {
    private let store = PostsDataStore()

    func add() async {
        let post = Post(title: "New post", date: .now)
        await store.add(post)
    }

    func fetchAll() async -> [Post] {
        let posts = await store.posts
        return Array(posts)
    }
}

In this case, let’s assume that the user attempts to add a post on one thread while fetching posts on another thread. By using an actor, we prevent nasty data races and keep our posts data clean. It also saves time debugging in Xcode, which usually isn’t fun.

That’s the Tea

Hopefully you can see how actors are extremely valuable for adapting your code to Swift’s new concurrency paradigm. Not only do they provide a simple type declaration to make thread-safe properties and methods, actors also allow for easy interfacing via async / await. Be sure to try them out in your next project!