Section 9/161 menit
9. Custom Global Actors
9. Custom Global Actors
Global actor adalah singleton isolation domain yang bisa di-annotate ke tipe, method, atau property, mirip @MainActor tapi didefinisikan sendiri.
Membuat Global Actor
swift
import Foundation
// 1. Definisikan actor singleton
actor DatabaseActorBody {
static let shared = DatabaseActorBody()
private init() {}
}
// 2. Buat global actor menggunakan @globalActor
@globalActor
struct DatabaseActor {
static let shared = DatabaseActorBody()
}
Menggunakan Custom Global Actor
swift
// Isolate seluruh class ke DatabaseActor
@DatabaseActor
class CoreDataStack {
var viewContext: NSManagedObjectContext
init() {
viewContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
}
func save() throws {
try viewContext.save() // ✓ — dijalankan di DatabaseActor
}
func fetch<T: NSManagedObject>(_ type: T.Type) -> [T] {
let request = T.fetchRequest()
return (try? viewContext.fetch(request) as? [T]) ?? []
}
}
// Isolate method spesifik
class DataRepository {
@DatabaseActor
func writeData(_ data: Data) async throws {
// Ini dijamin berjalan di DatabaseActor
}
@DatabaseActor
var cachedItems: [Item] = []
}
Use Case: Dedicated Serial Queue untuk Legacy Code
swift
// Isolate operasi file I/O ke dedicated executor
actor FileIOActorBody {
static let shared = FileIOActorBody()
private init() {}
}
@globalActor
struct FileIOActor {
static let shared = FileIOActorBody()
}
@FileIOActor
class FileManager {
private let queue = DispatchSerialQueue(label: "com.app.fileio")
func write(_ data: Data, to url: URL) throws {
try data.write(to: url)
}
func read(from url: URL) throws -> Data {
try Data(contentsOf: url)
}
}
// Semua akses file terjamin serial — tidak ada concurrent I/O
@FileIOActor
func batchSaveFiles(_ files: [(URL, Data)]) async throws {
for (url, data) in files {
try data.write(to: url)
}
}
Interaksi Antar Global Actors
swift
@MainActor
class ViewController {
@DatabaseActor
var databaseManager: CoreDataStack?
func loadData() async {
// Hop dari @MainActor ke @DatabaseActor secara implisit
let items = await databaseManager?.fetch(Item.self) ?? []
// Kembali ke @MainActor untuk update UI
updateUI(with: items) // ✓ — di @MainActor
}
}