Skip to main content

How to make parts of an actor nonisolated

About 3 minSwiftArticle(s)bloghackingwithswift.comcrashcourseswiftxcodeappstore

How to make parts of an actor nonisolated 관련

Swift Concurrency by Example

Back to Home

How to make parts of an actor nonisolated | Swift Concurrency by Example

How to make parts of an actor nonisolated

Updated for Xcode 15

All methods and mutable properties inside an actor are isolated to that actor by default, which means they cannot be accessed directly from code that’s external to the actor. Access to constant properties is automatically allowed because they are inherently safe from race conditions, but if you want you can make some methods excepted by using the nonisolated keyword.

Actor methods that are non-isolated can access other non-isolated state, such as constant properties or other methods that are marked non-isolated. However, they cannot directly access isolated state like an isolated actor method would; they need to use await instead.

To demonstrate non-isolated methods, we could write a User actor that has three properties: two constant strings for their username and password, and a variable Boolean to track whether they are online. Because password is constant, we could write a non-isolated method that returns the hash of that password using CryptoKit, like this:

import CryptoKit
import Foundation

actor User {
    let username: String
    let password: String
    var isOnline = false

    init(username: String, password: String) {
        self.username = username
        self.password = password
    }

    nonisolated func passwordHash() -> String {
        let passwordData = Data(password.utf8)
        let hash = SHA256.hash(data: passwordData)
        return hash.compactMap { String(format: "%02x", $0) }.joined()
    }
}

let user = User(username: "twostraws", password: "s3kr1t")
print(user.passwordHash())

Download this as an Xcode projectopen in new window

I’d like to pick out a handful of things in that code:

  1. Marking passwordHash() as nonisolated means that we can call it externally without using await.
  2. We can also use nonisolated with computed properties, which in the previous example would have made nonisolated var passwordHash: String. Stored properties may not be non-isolated.
  3. Non-isolated properties and methods can access only other non-isolated properties and methods, which in our case is a constant property. Swift will not let you ignore this rule.

Non-isolated methods are particularly useful when dealing with protocol conformances such as Hashable and Codable, where we must implement methods to be run from outside the actor.

For example, if we wanted to make our User actor conform to Codable, we’d need to implement encode(to:) ourselves as a non-isolated method like this:

actor User: Codable {
    enum CodingKeys: CodingKey {
        case username, password
    }

    let username: String
    let password: String
    var isOnline = false

    init(username: String, password: String) {
        self.username = username
        self.password = password
    }

    nonisolated func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(username, forKey: .username)
        try container.encode(password, forKey: .password)
    }
}

let user = User(username: "twostraws", password: "s3kr1t")

if let encoded = try? JSONEncoder().encode(user) {
    let json = String(decoding: encoded, as: UTF8.self)
    print(json)
}

Download this as an Xcode projectopen in new window

Similar solutions…
What is an actor and why does Swift have them? | Swift Concurrency by Example

What is an actor and why does Swift have them?
What is actor hopping and how can it cause problems? | Swift Concurrency by Example

What is actor hopping and how can it cause problems?
Important: Do not use an actor for your SwiftUI data models | Swift Concurrency by Example

Important: Do not use an actor for your SwiftUI data models
How to create and use an actor in Swift | Swift Concurrency by Example

How to create and use an actor in Swift
How to use @MainActor to run code on the main queue | Swift Concurrency by Example

How to use @MainActor to run code on the main queue

이찬희 (MarkiiimarK)
Never Stop Learning.