Section 15/161 menit

15. Real Use Cases

15. Real Use Cases

Use Case 1: Image Processing Pipeline dengan Region-Based Isolation

Pipeline yang mem-proses gambar besar secara background tanpa perlu membuat UIImage atau CGImage conform Sendable.

swift
import UIKit
import CoreImage

// UIImage dan CIImage tidak Sendable — tapi kita bisa transfer dengan region analysis
@MainActor
class ImageProcessingViewModel {
    var processedImage: UIImage?
    var isProcessing = false

    func applyFilter(to image: UIImage) async {
        isProcessing = true

        // Transfer image ke background — setelah ini, image tidak diakses di @MainActor lagi
        let result = await Task.detached {
            // Image hanya diakses di sini — region aman
            guard let ciImage = CIImage(image: image) else { return image }
            
            let context = CIContext()
            let filter = CIFilter.gaussianBlur()
            filter.inputImage = ciImage
            filter.radius = 10

            guard let outputImage = filter.outputImage,
                  let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else {
                return image
            }
            return UIImage(cgImage: cgImage)
        }.value

        processedImage = result
        isProcessing = false
    }
}

Use Case 2: Database Actor dengan Re-entrancy yang Aman

Database actor yang menangani concurrent queries tanpa duplikasi atau race condition.

swift
import Foundation

struct QueryResult {
    let rows: [[String: Any]]
    let executionTime: TimeInterval
}

actor DatabaseActor {
    private var connection: DatabaseConnection
    private var activeQueries: [String: Task<QueryResult, Error>] = [:]
    private var queryStats: [String: Int] = [:]  // Cache hit tracking

    init(connection: DatabaseConnection) {
        self.connection = connection
    }

    // Query dengan deduplication — concurrent identical query tidak di-execute dua kali
    func query(_ sql: String, parameters: [Any] = []) async throws -> QueryResult {
        let cacheKey = sql + parameters.map { "\($0)" }.joined()

        // Jika query identik sedang berjalan, tunggu hasilnya
        if let existing = activeQueries[cacheKey] {
            queryStats[cacheKey, default: 0] += 1
            return try await existing.value
        }

        // Daftarkan task SEBELUM suspension point
        let queryTask = Task<QueryResult, Error> {
            let start = Date()
            let rows = try await connection.execute(sql, parameters: parameters)
            return QueryResult(rows: rows, executionTime: Date().timeIntervalSince(start))
        }
        activeQueries[cacheKey] = queryTask

        do {
            let result = try await queryTask.value
            activeQueries.removeValue(forKey: cacheKey)
            return result
        } catch {
            activeQueries.removeValue(forKey: cacheKey)
            throw error
        }
    }

    var deduplicationStats: [String: Int] { queryStats }
}

Use Case 3: Multi-Isolation Architecture untuk App E-Commerce

Arsitektur yang memisahkan concerns ke isolation domain berbeda untuk performa dan keamanan.

swift
import Foundation
import CoreData

// Custom actor untuk operasi database
actor DatabaseActorBody {
    static let shared = DatabaseActorBody()
}

@globalActor
struct DB {
    static let shared = DatabaseActorBody()
}

// Custom actor untuk network operations
actor NetworkActorBody {
    static let shared = NetworkActorBody()
}

@globalActor
struct Net {
    static let shared = NetworkActorBody()
}

// Database layer — hanya berjalan di DB actor
@DB
class ProductRepository {
    private let context: NSManagedObjectContext

    init(context: NSManagedObjectContext) {
        self.context = context
    }

    func save(_ product: Product) throws {
        let entity = ProductEntity(context: context)
        entity.id = product.id
        entity.name = product.name
        entity.price = product.price
        try context.save()
    }

    func fetchAll() -> [Product] {
        let request = ProductEntity.fetchRequest()
        let entities = (try? context.fetch(request)) ?? []
        return entities.map { Product(id: $0.id!, name: $0.name!, price: $0.price) }
    }
}

// Network layer — hanya berjalan di Net actor
@Net
class ProductAPIClient {
    private let session = URLSession.shared

    func fetchProducts() async throws -> [Product] {
        let (data, _) = try await session.data(from: URL(string: "https://api.example.com/products")!)
        return try JSONDecoder().decode([Product].self, from: data)
    }

    func uploadOrder(_ order: Order) async throws -> OrderConfirmation {
        var request = URLRequest(url: URL(string: "https://api.example.com/orders")!)
        request.httpMethod = "POST"
        request.httpBody = try JSONEncoder().encode(order)
        let (data, _) = try await session.data(for: request)
        return try JSONDecoder().decode(OrderConfirmation.self, from: data)
    }
}

// Coordinator di @MainActor — orkestrasi tanpa langsung sentuh DB atau Network
@MainActor
class ProductCoordinator {
    private let repo: ProductRepository
    private let api: ProductAPIClient
    var products: [Product] = []
    var isLoading = false

    init(repo: ProductRepository, api: ProductAPIClient) {
        self.repo = repo
        self.api = api
    }

    func syncProducts() async {
        isLoading = true
        defer { isLoading = false }

        do {
            // Hop ke Net actor untuk fetch
            let remote = try await api.fetchProducts()

            // Hop ke DB actor untuk save
            for product in remote {
                try await repo.save(product)
            }

            // Kembali ke @MainActor untuk update UI
            products = await repo.fetchAll()
        } catch {
            print("Sync gagal: \(error)")
        }
    }
}