Section 9/103 menit

9. Hal yang Perlu Diperhatikan & Trade-Off

9. Hal yang Perlu Diperhatikan & Trade-Off

Hal yang Perlu Diperhatikan

1. SwiftSyntax Version Lock

SwiftSyntax API berubah setiap kali Swift toolchain berubah. Versi SwiftSyntax harus cocok dengan versi Swift yang digunakan:

swift
// Package.swift — versi harus sesuai dengan Swift toolchain
dependencies: [
    // Swift 5.9 → SwiftSyntax 509.x.x
    // Swift 5.10 → SwiftSyntax 510.x.x
    // Swift 6.0 → SwiftSyntax 600.x.x
    .package(
        url: "https://github.com/apple/swift-syntax.git",
        from: "600.0.0"  // untuk Swift 6.0
    ),
],

Risiko: Upgrade Xcode bisa break macro implementation. Wajib update Package.swift dan implementasi setiap kali upgrade Xcode major version.

2. Build Time Bertambah

Setiap macro yang di-expand menambah waktu kompilasi. Untuk project besar dengan banyak macro:

  • Profilkan waktu build dengan xcodebuild -showBuildTimingSummary
  • Pertimbangkan granularitas macro — macro yang terlalu besar memperlambat build lebih signifikan
  • Cache macro expansion tersedia di Xcode build system, tapi first-build selalu lebih lama

3. Debugging Membutuhkan "Expand Macro"

Kode yang di-generate macro tidak langsung terlihat. Untuk memeriksa hasil expansion di Xcode:

  1. Klik kanan pada penggunaan macro di editor
  2. Pilih "Expand Macro"
  3. Xcode akan menampilkan kode yang di-generate dalam inline view

Kamu tidak bisa langsung set breakpoint di kode hasil expansion dari editor biasa. Ini membatasi kemampuan step-through debugging.

4. Testing Macro adalah Keharusan

Tidak seperti kode biasa yang bisa di-test melalui behavior, macro harus di-test melalui assertMacroExpansion. Tanpa test, perubahan kecil pada implementasi bisa menghasilkan kode yang salah secara diam-diam.

swift
// Wajib: test setiap skenario expansion
// - happy path
// - edge case (struct kosong, generic type, nested type)
// - error case (macro digunakan salah)

5. Macro Tidak Bisa Akses Runtime Information

Macro hanya tahu apa yang ada di AST saat compile time. Hal-hal berikut tidak bisa dilakukan oleh macro:

swift
// ❌ Macro TIDAK bisa membaca nilai property saat runtime
// ❌ Macro TIDAK bisa melakukan reflection (Mirror) pada type yang belum diketahui
// ❌ Macro TIDAK bisa melakukan conditional logic berdasarkan OS version
// ❌ Macro TIDAK bisa baca environment variables saat runtime

6. Error Message Harus Informatif

Ketika macro gagal (karena digunakan salah), error message wajib jelas. Error yang membingungkan membuat developer frustrasi.

swift
// ❌ Error message buruk:
throw MacroError.failed

// ✓ Error message baik dengan konteks dan solusi:
context.diagnose(Diagnostic(
    node: node,
    message: MacroDiagnostic.requiresClass(typeName: typeName)
))

// Implementasi DiagnosticMessage:
struct MacroDiagnostic: DiagnosticMessage {
    static func requiresClass(typeName: String) -> MacroDiagnostic {
        MacroDiagnostic(
            message: "@Singleton hanya bisa digunakan pada class, bukan '\(typeName)' (struct/enum). Gunakan class alih-alih struct untuk service objects yang perlu singleton.",
            diagnosticID: .init(domain: "MyMacros", id: "requiresClass"),
            severity: .error
        )
    }

    let message: String
    let diagnosticID: MessageID
    let severity: DiagnosticSeverity
}

Trade-Off Mengimplementasikan Macro di Xcode Project

Aspek Keuntungan Kerugian
Boilerplate Berkurang drastis — decl 10 baris jadi 1 baris Macro itu sendiri perlu ditulis dan di-maintain
Type Safety Error terdeteksi di compile time, bukan runtime Membutuhkan pemahaman AST dan SwiftSyntax
Readability (API) Kode konsumen sangat bersih dan ekspresif Kode yang di-generate tidak langsung terlihat
Readability (Impl) Implementasi macro kompleks, sulit dipahami tanpa konteks SwiftSyntax
Build Time Bertambah, terutama pada first-build dan project besar
Debugging Step-through debugging terbatas pada kode hasil expansion
Maintenance Harus update setiap upgrade Xcode/Swift toolchain
Onboarding Developer baru perlu belajar SwiftSyntax sebelum bisa contribute ke macro
Testing Unit testable via assertMacroExpansion Harus test setiap skenario expansion secara eksplisit
Distribution Bisa didistribusikan sebagai SPM package Menambah dependency baru ke project
IDE Support "Expand Macro" di Xcode membantu inspect Tidak semua IDE/tooling mendukung macro inspection

Kapan Trade-Off Sebanding?

Macro paling menguntungkan ketika:

swift
Jumlah penggunaan macro × Boilerplate yang dihemat per penggunaan
    > Waktu membuat + waktu maintain macro

Contoh perhitungan kasar:

  • Membuat @AutoEquatable membutuhkan 2 hari kerja
  • Digunakan di 50 struct, masing-masing menghemat 15 baris kode
  • Total penghematan: 750 baris kode yang tidak perlu ditulis dan di-maintain
  • Kesimpulan: sebanding

Sebaliknya:

  • Membuat @AddPrefixHello membutuhkan 1 hari kerja
  • Digunakan di 2 tempat, masing-masing menghemat 1 baris kode
  • Total penghematan: 2 baris kode
  • Kesimpulan: tidak sebanding — gunakan fungsi biasa