Section 5/132 menit

5. Region-Based Isolation (Swift 6 Baru)

5. Region-Based Isolation (Swift 6 Baru)

5.1 Masalah yang Dipecahkan

Sebelum Swift 6, aturannya binary: sebuah nilai harus Sendable untuk dikirim ke isolation domain lain. Ini terlalu ketat dalam banyak kasus.

Pertimbangkan:

swift
func buildRequest() -> URLRequest {
    var request = URLRequest(url: someURL)
    request.httpMethod = "POST"
    request.httpBody = someData
    return request
}

// URLRequest bukan Sendable (di Swift 5.x)
// Tapi kita tahu bahwa setelah kita "kirim" ke network task,
// kita tidak akan mengakses request itu lagi!

// Swift 5.x: ERROR — URLRequest tidak Sendable
Task { try await URLSession.shared.data(for: buildRequest()) }

Aturan Sendable terlalu keras di sini karena kita tahu secara statik bahwa tidak ada concurrent access — kita tidak menyimpan referensi lain ke request itu.

5.2 Teori: Isolation Regions

Swift 6 memperkenalkan analisis region-based isolation — compiler melacak "region" atau "kelompok kepemilikan" setiap nilai, bukan hanya tipenya.

Sebuah isolation region adalah sekumpulan nilai yang saling terhubung melalui referensi. Jika dua nilai bisa saling menjangkau satu sama lain (misalnya object graph), mereka berada dalam region yang sama.

swift
class Node {
    var next: Node?
    var value: Int
}

var head = Node(value: 1)         // head
head.next = Node(value: 2)        // head.next
head.next?.next = Node(value: 3)  // head.next.next

// Ketiga node ini berada dalam SATU region
// karena mereka terhubung melalui referensi 'next'

Ide kunci: Jika sebuah region tidak lagi dapat diakses dari konteks asal setelah "dikirim", maka aman untuk dikirim ke isolation domain lain — meskipun nilainya tidak Sendable.

5.3 sending Keyword

sending parameter menandai bahwa nilai ditransfer — kepemilikannya berpindah ke callee. Setelah transfer, caller tidak boleh mengakses nilai tersebut lagi:

swift
struct LargeModel {   // bukan Sendable
    var items: [String] = []
    var metadata: [String: Any] = [:]
}

// 'sending' = "saya mengambil alih kepemilikan model ini"
func processInBackground(_ model: sending LargeModel) async {
    // model sekarang milik fungsi ini
    // caller tidak bisa akses model setelah memanggil ini
    var processed = model
    processed.items.sort()
    await persist(processed)
}

// Penggunaan:
var model = LargeModel()
model.items = ["c", "a", "b"]

await processInBackground(model)  // transfer terjadi di sini

// ❌ ERROR: compiler tahu 'model' sudah di-transfer
// print(model.items)  // 'model' digunakan setelah dikirim ke isolation lain

Analogi: Ini seperti move semantics di C++ atau move di Rust. Nilai "dipindahkan", bukan "disalin" atau "dibagi".

Kapan gunakan sending:

  • Fungsi yang mengkonsumsi nilai dan tidak perlu dikembalikan
  • Pipeline processing: data masuk, diproses, tidak perlu kembali
  • Ketika kamu ingin izinkan tipe non-Sendable melewati isolation boundary secara satu arah

Kapan TIDAK gunakan sending:

  • Ketika caller masih butuh akses ke nilai setelah call
  • Ketika nilai perlu dibaca dari beberapa tempat — gunakan Sendable struct sebagai gantinya

5.4 Mengapa Ini Penting di Praktik

swift
// Swift 5.9 → Swift 6: Lebih banyak kode yang compile tanpa perubahan

// Sebelumnya butuh workaround
func sendRequest(_ request: URLRequest) async throws -> Data {
    let (data, _) = try await URLSession.shared.data(for: request)
    return data
}

// Swift 6: 'sending' memungkinkan ini secara eksplisit
func sendRequest(_ request: sending URLRequest) async throws -> Data {
    let (data, _) = try await URLSession.shared.data(for: request)
    return data
}

Region-based isolation adalah tentang memberi compiler informasi yang lebih kaya tentang kapan nilai bisa diakses, bukan hanya bagaimana nilai itu direpresentasikan di memori.