Section 7/171 menit

7. Best Practice: Sendable & Cross-Boundary Transfer

7. Best Practice: Sendable & Cross-Boundary Transfer

BP 7.1: Default Tipe Public ke Sendable

Untuk library / framework: setiap type public yang merupakan value (struct/enum) sebaiknya eksplisit konform Sendable. Konsumen library jadi tahu mereka bisa lewatkan tipe ini cross-isolation.

swift
// ✓ Eksplisit Sendable di API publik
public struct UserSnapshot: Sendable, Identifiable, Equatable {
    public let id: UUID
    public let name: String
    public let lastSeen: Date
}

BP 7.2: @unchecked Sendable Hanya dengan Synchronization

Pernyataan @unchecked Sendable adalah janji kepada compiler. Jangan buat janji kosong.

swift
// ❌ Janji kosong
final class Cache: @unchecked Sendable {
    var data: [String: Any] = [:]  // tidak ada lock, race waiting to happen
}

// ✓ Dengan lock manual
final class Cache: @unchecked Sendable {
    private let lock = NSLock()
    private var data: [String: Any] = [:]

    func value(for key: String) -> Any? {
        lock.withLock { data[key] }
    }

    func setValue(_ value: Any, for key: String) {
        lock.withLock { data[key] = value }
    }
}

// ✓ Atau prefer actor (lebih sulit salah)
actor Cache {
    var data: [String: any Sendable] = [:]
}

BP 7.3: Pakai sending untuk Builder Non-Sendable

Builder mutable yang baru dibuat ergonomis untuk UI tapi tidak Sendable. Pakai sending untuk transfer-sekali ke actor.

swift
final class ReportBuilder {
    var title: String = ""
    var sections: [Section] = []
    // tidak Sendable: var properties
}

actor ReportEngine {
    func generate(from builder: sending ReportBuilder) async -> Report {
        // builder ter-transfer, caller tidak boleh akses lagi
        return Report(title: builder.title, sections: builder.sections)
    }
}

BP 7.4: Conditional Sendable untuk Generic Wrapper

Container generic yang membungkus payload sebaiknya Sendable secara kondisional, bukan absolut.

swift
// ✓ Conditional
struct Cached<T> {
    let value: T
    let timestamp: Date
}

extension Cached: Sendable where T: Sendable { }
extension Cached: Equatable where T: Equatable { }