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 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
SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.