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
    }
}