Section 2/181 menit

2. Masalah yang Dipecahkan

2. Masalah yang Dipecahkan

SiriKit: Boilerplate Besar, Fleksibilitas Rendah

swift
// ❌ TANPA App Intents: SiriKit INIntent — file .intentdefinition + code gen + delegate
// File: OrderCoffeeIntent.intentdefinition (XML, dikelola Xcode)
// + INExtension handler di separate target
// + NSExtensionPrincipalClass di Info.plist

class OrderCoffeeIntentHandler: NSObject, OrderCoffeeIntentHandling {
    // Confirm phase — wajib implement
    func confirm(intent: OrderCoffeeIntent, completion: @escaping (OrderCoffeeIntentResponse) -> Void) {
        completion(OrderCoffeeIntentResponse(code: .ready, userActivity: nil))
    }
    
    // Handle phase — wajib implement
    func handle(intent: OrderCoffeeIntent, completion: @escaping (OrderCoffeeIntentResponse) -> Void) {
        // Akses data dari UI app via shared container atau XPC
        // Karena Intent Extension berjalan di separate process!
        let sharedDefaults = UserDefaults(suiteName: "group.com.myapp")
        let menuItems = sharedDefaults?.array(forKey: "menu") as? [[String: Any]] ?? []
        
        // Masalah: separate process = tidak bisa akses langsung model layer app
        // Masalah: INIntent hanya untuk domain tertentu (Messages, Calls, dll)
        // Masalah: Customization sangat terbatas
        completion(OrderCoffeeIntentResponse.success(totalPrice: 25000))
    }
    
    // Resolve setiap parameter secara terpisah
    func resolveDrinkType(for intent: OrderCoffeeIntent,
                         with completion: @escaping (DrinkTypeResolutionResult) -> Void) {
        guard let drinkType = intent.drinkType else {
            completion(.needsValue())
            return
        }
        completion(.success(with: drinkType))
    }
}
swift
// ✓ DENGAN App Intents: pure Swift struct, berjalan in-process, tidak ada code gen
struct OrderCoffeeIntent: AppIntent {
    static var title: LocalizedStringResource = "Pesan Kopi"
    static var description = IntentDescription("Pesan kopi favorit kamu dari cafe terdekat")
    
    @Parameter(title: "Jenis Minuman")
    var drinkType: DrinkEntity
    
    @Parameter(title: "Ukuran", default: .medium)
    var size: CoffeeSize
    
    func perform() async throws -> some IntentResult & ProvidesDialog {
        // Langsung akses service layer app — in-process!
        let order = try await CoffeeService.shared.placeOrder(drink: drinkType, size: size)
        return .result(dialog: "Pesanan \(drinkType.name) \(size.rawValue) sudah diterima!")
    }
}

NSUserActivity: Tidak Ada Parameter Dinamis

swift
// ❌ TANPA App Intents: NSUserActivity — tidak ada parameter interaktif
class SceneDelegate: UIResponder, UISceneDelegate {
    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        // Hanya bisa pass data statis via userInfo dictionary
        // Tidak bisa tanya user parameter saat runtime
        // Tidak ada parameter completion / disambiguation
        guard let productID = userActivity.userInfo?["productID"] as? String else { return }
        navigateToProduct(id: productID)
    }
}
swift
// ✓ DENGAN App Intents: parameter interaktif, Siri bisa tanya user jika perlu
struct OpenProductIntent: AppIntent {
    static var title: LocalizedStringResource = "Buka Produk"
    
    @Parameter(title: "Produk")
    var product: ProductEntity  // Siri tanya "Produk mana?" jika tidak disebut
    
    static var openAppWhenRun: Bool = true
    
    func perform() async throws -> some IntentResult {
        await MainActor.run {
            NavigationService.shared.navigate(to: product)
        }
        return .result()
    }
}