AppIntent perform method not called.

We have a widget bundle with multiple widgets. I'm adding a widget that is interactive (iOS 17 and higher). Our widget code is in a static library that gets linked into the widget extension target in our main app Xcode project. I have SwiftUI buttons constructed with the intent constructor in our UI See https://developer.apple.com/documentation/swiftui/button/init(intent🏷️ )

When I press the button the timeline refreshes (conforming to TimelineProvider) but the perform method doesn't seem to be called. I've seen multiple pieces of advice and none of them seem to work. I've tried on a physical device and a simulator. I've tried adding an AppIntentsPackage. I've tried including the AppIntent code in the app and the widget. I've tried setting the openAppWhenRun to true and false and not setting it at all. I've tried simplifying the intent to just printing out a line to the console and returning a result. At this point I have no idea how to debug this and I don't know what else to try.

I appreciate any helpful advice at this point.

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

I was finally able to get the TestIntent to work in the main app but still can't get the widget linked in from the static library to work.

Can you file a bug report with sysdiagnose?

AppIntent perform method not called.
 
 
Q