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