I created a simple TestWidget in my main app
- When I use the configurable widget (i.e. TestWidgetConfigurable) only the placeholder API gets called on TestEntryProvider and the widget shows a placeholder
- When I use the statically configured widget (i.e. TestWidget) then my timeline api gets called on TestEntryProvider. However when I tap the button in TestWidgetButtonView the perform button in TestIntent never gets called but the widget does refresh / reload the timeline.
import AppIntents
import WidgetKit
import SwiftUI
@available(iOS 17.0.0, *)
struct TestWidgetConfigurationIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource { "Alexa Test Widget" }
static var description: IntentDescription { "Try to get intents working" }
}
public struct TestEntry: TimelineEntry {
public let date: Date
public init(date: Date = .now) {
self.date = date
}
}
@available(iOS 17.0.0, *)
struct TestEntryProvider: AppIntentTimelineProvider, TimelineProvider {
typealias Entry = TestEntry
typealias Intent = TestWidgetConfigurationIntent
func placeholder(in context: Context) -> TestEntry {
TestEntry()
}
func snapshot(in context: Context) async -> TestEntry {
TestEntry()
}
func snapshot(for configuration: TestWidgetConfigurationIntent, in context: Context) async -> TestEntry {
await snapshot(in: context)
}
func timeline(in context: Context) -> Timeline<TestEntry> {
let currentDate = Date.now
let nextRefresh = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
let entry = TestEntry()
let entries: [TestEntry] = [entry]
let timeline = Timeline(entries: entries, policy: .after(nextRefresh))
return timeline
}
func timeline(for configuration: TestWidgetConfigurationIntent, in context: Context) async -> Timeline<TestEntry> {
timeline(in: context)
}
func getSnapshot(in context: Context, completion: @escaping @Sendable (TestEntry) -> Void) {
completion(TestEntry())
}
func getTimeline(in context: Context, completion: @escaping @Sendable (Timeline<TestEntry>) -> Void) {
completion(timeline(in: context))
}
}
@available(iOS 17.0.0, *)
public struct TestWidget: Widget {
static let widgetKind = "TestWidget"
let kind: String = Self.widgetKind
public init() {}
public var body: some WidgetConfiguration {
StaticConfiguration(
kind: kind,
provider: TestEntryProvider()
) { entry in
TestWidgetFamilyView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("Test Widget")
.description("Test widget to try to get intents working")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
}
}
@available(iOS 17.0.0, *)
public struct TestWidgetConfigurable: Widget {
// Note: This is the same as AlexaSmartHomeWidgetConfigurable.widgetKind
static let widgetKind = "TestWidget"
let kind: String = Self.widgetKind
public init() {}
public var body: some WidgetConfiguration {
AppIntentConfiguration(
kind: kind,
intent: TestWidgetConfigurationIntent.self,
provider: TestEntryProvider()
) { entry in
TestWidgetFamilyView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("Test Widget")
.description("Test widget to try to get intents working")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
}
}
public struct TestIntent: AppIntent {
public static var title = LocalizedStringResource("Test Intent")
public init() {}
public func perform() async throws -> some IntentResult {
print(String(describing: Self.self) + " perform")
return .result()
}
}
@available(iOS 17.0.0, *)
public struct TestWidgetFamilyView: View {
var entry: TestEntry
@Environment(\.widgetFamily) var family: WidgetFamily
private var defaultView: TestWidgetButtonView {
return TestWidgetButtonView(entry: entry)
}
public var body: some View {
switch family {
case .systemSmall:
TestWidgetButtonView(entry: entry)
case .systemMedium:
TestWidgetButtonView(entry: entry)
case .systemLarge:
TestWidgetButtonView(entry: entry)
default:
defaultView
}
}
}
@available(iOS 17.0.0, *)
public struct TestWidgetButtonView: View {
var entry: TestEntry
public var body: some View {
Button(intent: TestIntent()) {
Text("Test \(entry.date)")
}
}
}