Section 6/101 menit

6. Kapan Harus Menggunakan Macro

6. Kapan Harus Menggunakan Macro

Skenario 1: Eliminasi Boilerplate Berulang

Ketika kamu menulis pola yang sama berulang kali di banyak type, macro adalah pilihan tepat.

swift
// Masalah: setiap ViewModel perlu setup yang sama
class LoginViewModel: ObservableObject {
    @Published var username: String = ""
    @Published var password: String = ""
    @Published var isLoading: Bool = false
    @Published var errorMessage: String? = nil
}

class RegisterViewModel: ObservableObject {
    @Published var email: String = ""
    @Published var name: String = ""
    @Published var isLoading: Bool = false
    @Published var errorMessage: String? = nil
}

// ✓ Dengan macro @ViewModel yang generate isLoading + errorMessage otomatis:
@ViewModel
class LoginViewModel: ObservableObject {
    @Published var username: String = ""
    @Published var password: String = ""
    // isLoading dan errorMessage di-generate oleh macro
}

Skenario 2: Validasi dan Type Safety di Compile Time

Macro dapat memvalidasi argumen di compile time, mencegah bug runtime.

swift
// Masalah: URL yang salah baru ketahuan saat runtime
let badURL = URL(string: "not a valid url")!  // crash di runtime!

// ✓ Dengan macro #URL yang validasi di compile time:
let apiURL = #URL("https://api.example.com/v1/users")
// ❌ Compile error jika URL tidak valid — bukan runtime crash

Skenario 3: Logging dan Tracing Otomatis

Menambahkan logging ke banyak fungsi tanpa mengubah implementasi aslinya.

swift
// Masalah: harus tambah logging manual ke setiap fungsi
func fetchUser(id: String) async throws -> User {
    logger.debug("fetchUser called with id: \(id)")
    let start = Date()
    defer { logger.debug("fetchUser completed in \(Date().timeIntervalSince(start))s") }
    // ... implementasi
}

// ✓ Dengan macro @Logged — logging otomatis tanpa boilerplate:
@Logged
func fetchUser(id: String) async throws -> User {
    // ... hanya implementasi, logging di-generate oleh macro
}

Skenario 4: Dependency Injection Container

Generate dependency registration dan resolution secara otomatis.

swift
// ✓ Macro @Injectable generate register() dan resolve() otomatis
@Injectable
class NetworkService {
    init(baseURL: URL, session: URLSession) { ... }
}

// Macro generate:
// static func register(in container: DIContainer) { ... }
// static func resolve(from container: DIContainer) -> NetworkService { ... }

Skenario 5: Async Wrapper dari Completion Handler

Untuk bekerja dengan API lama yang menggunakan completion handler.

swift
// API lama (third-party SDK yang tidak bisa diubah)
func fetchData(completion: @escaping (Data?, Error?) -> Void) { ... }

// ✓ Macro @AddAsync generate versi async secara otomatis
@AddAsync
func fetchData(completion: @escaping (Data?, Error?) -> Void) { ... }

// Macro generate:
// func fetchData() async throws -> Data { ... }