Section 10/181 menit

10. WidgetKit Integration: Interactive Widgets

10. WidgetKit Integration: Interactive Widgets

iOS 17+ memungkinkan Widget memiliki interactive button yang menjalankan App Intent langsung tanpa membuka app.

Widget dengan Button

swift
import WidgetKit
import SwiftUI
import AppIntents

// Intent khusus untuk widget — bisa sangat ringkas
struct ToggleTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Toggle Tugas"
    
    @Parameter(title: "Task ID")
    var taskID: UUID
    
    func perform() async throws -> some IntentResult {
        try await TaskRepository.shared.toggle(id: taskID)
        // Widget otomatis refresh setelah perform() selesai
        return .result()
    }
}

// Widget Entry View dengan Button
struct TaskWidgetEntryView: View {
    var entry: TaskWidgetEntry
    
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Tugas Hari Ini")
                .font(.headline)
            
            ForEach(entry.tasks.prefix(3)) { task in
                HStack {
                    // Button yang trigger App Intent — tidak buka app
                    Button(intent: ToggleTaskIntent(taskID: task.id)) {
                        Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
                            .foregroundColor(task.isCompleted ? .green : .gray)
                    }
                    .buttonStyle(.plain)
                    
                    Text(task.title)
                        .strikethrough(task.isCompleted)
                        .foregroundColor(task.isCompleted ? .secondary : .primary)
                }
            }
        }
        .padding()
    }
}

// Widget konfigurasi dengan AppIntentConfiguration
struct TaskWidget: Widget {
    var body: some WidgetConfiguration {
        // AppIntentConfiguration: user bisa konfigurasi widget via intent
        AppIntentConfiguration(
            kind: "TaskWidget",
            intent: TaskWidgetConfigurationIntent.self,
            provider: TaskWidgetProvider()
        ) { entry in
            TaskWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("Tugas")
        .description("Tampilkan dan kelola tugas kamu.")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

// Intent untuk konfigurasi widget (ditampilkan saat user long-press widget)
struct TaskWidgetConfigurationIntent: WidgetConfigurationIntent {
    static var title: LocalizedStringResource = "Konfigurasi Widget Tugas"
    
    @Parameter(title: "Tampilkan", default: .pending)
    var filter: TaskFilter
    
    @Parameter(title: "Maks Item", default: 3, inclusiveRange: (1, 5))
    var maxItems: Int
}

enum TaskFilter: String, AppEnum {
    case all, pending, completed
    
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Filter Tugas"
    static var caseDisplayRepresentations: [TaskFilter: DisplayRepresentation] = [
        .all:       .init(title: "Semua"),
        .pending:   .init(title: "Belum Selesai"),
        .completed: .init(title: "Sudah Selesai")
    ]
}