Section 10/161 menit
10. Custom Global Actors
10. Custom Global Actors
Global actor adalah singleton actor yang bisa di-annotate ke tipe dan method — mirip @MainActor tapi custom.
Membuat Global Actor
swift
// Custom global actor untuk database access
@globalActor
actor DatabaseActor {
static let shared = DatabaseActor()
// Isolasi database access ke satu thread
private let queue = DispatchQueue(label: "database", qos: .userInitiated)
nonisolated var unownedExecutor: UnownedSerialExecutor {
DatabaseExecutor.shared.asUnownedSerialExecutor()
}
}
final class DatabaseExecutor: SerialExecutor, @unchecked Sendable {
static let shared = DatabaseExecutor()
private let queue = DispatchQueue(label: "database.executor", qos: .userInitiated)
func enqueue(_ job: consuming ExecutorJob) {
let unownedJob = UnownedJob(job)
queue.async {
unownedJob.runSynchronously(on: self.asUnownedSerialExecutor())
}
}
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
}
}
// Annotate class dengan custom global actor
@DatabaseActor
final class UserRepository {
private var db: DatabaseConnection
init(db: DatabaseConnection) {
self.db = db
}
// Method ini otomatis berjalan di DatabaseActor
func fetchUser(id: String) async throws -> User {
try db.query("SELECT * FROM users WHERE id = ?", id)
}
func saveUser(_ user: User) async throws {
try db.execute("INSERT INTO users ...", user)
}
}
// Bisa juga annotate method individual
class DataService {
@DatabaseActor
func performDatabaseOperation() async {
// Method ini berjalan di DatabaseActor meski class-nya tidak di-annotate
}
@MainActor
func updateUI() {
// Berjalan di main actor
}
func bridgeBetweenActors() async {
await performDatabaseOperation() // hop ke DatabaseActor
await updateUI() // hop ke MainActor
}
}
class DatabaseConnection {
func query<T>(_ sql: String, _ params: Any...) throws -> T { fatalError() }
func execute(_ sql: String, _ params: Any...) throws {}
}