Section 8/141 menit
8. App Intents: Widget yang Dapat Dikonfigurasi
8. App Intents: Widget yang Dapat Dikonfigurasi
App Intents (iOS 17+) menggantikan SiriKit Intent Definition Files yang lama untuk konfigurasi widget.
Definisikan Intent Konfigurasi
swift
import AppIntents
import WidgetKit
// Intent yang mendefinisikan parameter konfigurasi widget
struct HabitSelectionIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Pilih Habit"
static var description = IntentDescription("Pilih habit yang ingin ditampilkan di widget")
@Parameter(title: "Habit", default: nil)
var habit: HabitEntity?
}
// Entity yang merepresentasikan Habit (untuk picker di widget config)
struct HabitEntity: AppEntity {
let id: String
let name: String
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Habit"
static var defaultQuery = HabitEntityQuery()
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(name)")
}
}
struct HabitEntityQuery: EntityQuery {
func entities(for identifiers: [HabitEntity.ID]) async throws -> [HabitEntity] {
// Ambil habits berdasarkan ID dari shared storage
let sharedDefaults = UserDefaults(suiteName: "group.com.perusahaan.namaapp")
let allHabits = sharedDefaults?.stringArray(forKey: "allHabits") ?? []
return allHabits
.filter { identifiers.contains($0) }
.map { HabitEntity(id: $0, name: $0) }
}
func suggestedEntities() async throws -> [HabitEntity] {
// Tampilkan semua habit yang tersedia di picker
let sharedDefaults = UserDefaults(suiteName: "group.com.perusahaan.namaapp")
let allHabits = sharedDefaults?.stringArray(forKey: "allHabits") ?? []
return allHabits.map { HabitEntity(id: $0, name: $0) }
}
}
AppIntentTimelineProvider
swift
// Provider yang menggunakan intent untuk konfigurasi
struct ConfigurableHabitProvider: AppIntentTimelineProvider {
// Intent konfigurasi yang dipilih user di widget gallery
typealias Intent = HabitSelectionIntent
func placeholder(in context: Context) -> HabitEntry {
HabitEntry(date: Date(), habitName: "Olahraga", isCompleted: false, streak: 7, habitID: "placeholder")
}
func snapshot(for configuration: HabitSelectionIntent, in context: Context) async -> HabitEntry {
let name = configuration.habit?.name ?? "Olahraga"
return HabitEntry(date: Date(), habitName: name, isCompleted: true, streak: 14, habitID: configuration.habit?.id ?? "")
}
func timeline(for configuration: HabitSelectionIntent, in context: Context) async -> Timeline<HabitEntry> {
let sharedDefaults = UserDefaults(suiteName: "group.com.perusahaan.namaapp")
let selectedHabitID = configuration.habit?.id ?? ""
let isCompleted = sharedDefaults?.bool(forKey: "habitCompleted_\(selectedHabitID)") ?? false
let streak = sharedDefaults?.integer(forKey: "streak_\(selectedHabitID)") ?? 0
let name = configuration.habit?.name ?? "Pilih Habit"
let entry = HabitEntry(date: Date(), habitName: name, isCompleted: isCompleted, streak: streak, habitID: selectedHabitID)
let midnight = Calendar.current.startOfDay(for: Date().addingTimeInterval(86400))
return Timeline(entries: [entry], policy: .after(midnight))
}
}
// Widget menggunakan AppIntentConfiguration
struct ConfigurableHabitWidget: Widget {
var body: some WidgetConfiguration {
AppIntentConfiguration(
kind: "ConfigurableHabitWidget",
intent: HabitSelectionIntent.self,
provider: ConfigurableHabitProvider()
) { entry in
InteractiveHabitView(entry: entry)
}
.configurationDisplayName("Habit Tracker")
.description("Pilih dan pantau satu habit spesifik.")
.supportedFamilies([.systemSmall, .systemMedium])
}
}