1

I want to add shortcut and Siri support using the AppIntents framework. Running my intent using shortcuts or from spotlight works fine, as the touch based UI for the disambiguation is shown. However, when I ask Siri to perform this action, she gets into a loop of asking me the question to set the parameter.

My AppIntent is implemented as following:

struct StartSessionIntent: AppIntent {
    static var title: LocalizedStringResource = "start_recording"

    @Parameter(title: "activity", requestValueDialog: IntentDialog("which_activity"))
    var activity: ActivityEntity

    @MainActor
    func perform() async throws -> some IntentResult & ProvidesDialog {
        let activityToSelect: ActivityEntity = self.activity
        guard let selectedActivity = Activity[activityToSelect.name] else {
            return .result(dialog: "activity_not_found")
        }
        ...
        return .result(dialog: "recording_started (selectedActivity.name.localized())")
    }
}

The ActivityEntity is implemented like this:

struct ActivityEntity: AppEntity {
    static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "activity")
    typealias DefaultQuery = MobilityActivityQuery
    static var defaultQuery: MobilityActivityQuery = MobilityActivityQuery()

    var id: String
    var name: String
    var icon: String

    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(self.name.localized())", image: .init(systemName: self.icon))
    }
}

struct MobilityActivityQuery: EntityQuery {
    func entities(for identifiers: [String]) async throws -> [ActivityEntity] {
        Activity.all()?.compactMap({ activity in
            identifiers.contains(where: { $0 == activity.name }) ? ActivityEntity(id: activity.name, name: activity.name, icon: activity.icon) : nil
        }) ?? []
    }

    func suggestedEntities() async throws -> [ActivityEntity] {
        Activity.all()?.compactMap({ activity in
            ActivityEntity(id: activity.name, name: activity.name, icon: activity.icon)
        }) ?? []
    }
}

Has anyone an idea what might be causing this and how I can fix this behavior? Thanks in advance.

DeMo
  • 47
  • 1
  • 7

1 Answers1

1

I can see two possible reasons why Siri might be asking you to set the parameter for your AppIntent multiple times.

  1. The first possibility is that you have not set the default property for your @Parameter. This property tells Siri what value to use for the parameter if the user does not specify one. If you do not set this property, Siri will always ask the user to set the parameter.

To fix this, you can set the default property to the default value for your parameter. For example, if your parameter is a string, you could set the default property to an empty string.

  1. The second possibility is that you are using a custom IntentDialog for your @Parameter. If you are using a custom IntentDialog, you need to make sure that the dialog includes a way for the user to cancel the request. If the user cancels the request, Siri will not be able to set the parameter and will ask the user to set it again.

To fix this, you can add a cancel button to your IntentDialog. This will allow the user to cancel the request without setting the parameter.

Here is an example of how you could fix the problem by setting the default property:

struct StartSessionIntent: AppIntent {
    static var title: LocalizedStringResource = "start_recording"

    @Parameter(
        title: "activity",
        requestValueDialog: IntentDialog("which_activity"),
        default: ""
    )
    var activity: String

    @MainActor
    func perform() async throws -> some IntentResult & ProvidesDialog {
        let activityToSelect: ActivityEntity = Activity[activity] ?? ActivityEntity(id: "", name: "", icon: "")
        guard let selectedActivity = Activity[activityToSelect.name] else {
            return .result(dialog: "activity_not_found")
        }
        ...
        return .result(dialog: "recording_started \(selectedActivity.name.localized())")
    }
}


Here is an example of how you could fix the problem by adding a `cancel` button to your `IntentDialog`:


struct StartSessionIntent: AppIntent {
    static var title: LocalizedStringResource = "start_recording"

    @Parameter(
        title: "activity",
        requestValueDialog: IntentDialog("which_activity", buttons: [.cancel()])
    )
    var activity: String

    @MainActor
    func perform() async throws -> some IntentResult & ProvidesDialog {
        let activityToSelect: ActivityEntity = Activity[activity] ?? ActivityEntity(id: "", name: "", icon: "")
        guard let selectedActivity = Activity[activityToSelect.name] else {
            return .result(dialog: "activity_not_found")
        }
        ...
        return .result(dialog: "recording_started \(selectedActivity.name.localized())")
    }
}

I hope this helps!

peterdsp
  • 29
  • 4
  • unfortunately neither of your ideas worked for me. Since I am using a custom AppEntity I somehow can't use a default value, as it needs to be present at compile time (which it somehow isn't). When I say "cancel" to Siri, the request is canceled without a problem, but when I am not cancelling the request, Siri asks again and again for the parameter – DeMo Jun 20 '23 at 21:01