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