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:
// 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:
- Klik kanan pada penggunaan macro di editor
- Pilih "Expand Macro"
- 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.
// 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:
// ❌ 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.
// ❌ 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:
Jumlah penggunaan macro × Boilerplate yang dihemat per penggunaan
> Waktu membuat + waktu maintain macro
Contoh perhitungan kasar:
- Membuat
@AutoEquatablemembutuhkan 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
@AddPrefixHellomembutuhkan 1 hari kerja - Digunakan di 2 tempat, masing-masing menghemat 1 baris kode
- Total penghematan: 2 baris kode
- Kesimpulan: tidak sebanding — gunakan fungsi biasa