Updated for Xcode 14.2
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 project
I’d like to pick out a handful of things in that code:
passwordHash()
as nonisolated
means that we can call it externally without using await
.nonisolated
with computed properties, which in the previous example would have made nonisolated var passwordHash: String
. Stored properties may not be non-isolated.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 project
SAVE 50% To celebrate WWDC23, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.
Link copied to your pasteboard.