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")
]
}