Dynamic app shortcuts using AppShortcutsProvider and configurable AppIntent

Hello everyone,

I want to add preconfigured app shortcuts for my app using an AppShortcutsProvider implementation. I have been following this demo from WWDC22: https://developer.apple.com/wwdc22/10170

All looks pretty straight forward.

However, I would like the preconfigured shortcuts to be based on the state of the data in the app: adding one shortcut for every user-entered item in a list.

Now, this might be due to the fact that I'm a Swift novice, but I can't figure out how to do that from a AppShortcutsBuilder property as I can't create the AppShortcut instances in a for loop. I have failed to find any resources online on this. I assume I would need the ForEach equivalent of the ViewBuilder? Is anything like that available for AppShortcutsBuilder?

I have also stumbled on this: https://developer.apple.com/documentation/sirikit/offering_actions_in_the_shortcuts_app

But this solution seems to only work with INIntents rather than AppIntents. Is there a setShortcutSuggestions equivalent for AppIntents that I could call when the contents of my list change?

Replies

If you look at the definition of AppShortcutsProvider, you see it a {get} and it uses @AppShortcutsBuilder for special formatting of the shortcuts. So, you'll have to eternalize your conditional definition of appshortcuts.

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public protocol AppShortcutsProvider {

    @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { get }

    /// The background color of the tile that Shortcuts displays for each of the app's App Shortcuts.
    static var shortcutTileColor: ShortcutTileColor { get }
}

Here's one way to accomplish this:

struct EventManagerShortcuts: AppShortcutsProvider {
    
    static var shortcutTileColor: ShortcutTileColor = .lime
    // Max 10 shortcut actions per App. We need to make them dynamic by role and condition.
    static var appShortcuts: [AppShortcut] {
        return  EventManagerShortcuts().createAppShortcuts()
    }
    
    private func createAppShortcuts() ->[AppShortcut] {
        var appshortcuts = [AppShortcut]()
        // Access the ViewModel
        let viewModel = HomePage()
        //  Filter the objects we want to show as Shortcuts
        let activeHomePageElements = viewModel.homePageElements
            .filter({
                $0.systemState != .idle &&
                $0.userState != .idle
            })
        //. use std syntax for the loop to create shortcuts
        for element in activeHomePageElements {
            switch element.action {
            case .AssignAgenda:
                                let NextMeetingShortcutPhrases : [AppShortcutPhrase<NextMeetingShortcut>] = [
                    "Start a business event with \(.applicationName)",
                    "\(.applicationName) business meeting",
                    "Event for \(.applicationName)",
                    "Start a Salesforce event with \(.applicationName)"
                ]
                let nextMeetingShortcut = AppShortcut(intent: NextMeetingShortcut(),
                                                      phrases: NextMeetingShortcutPhrases,
                                                      // shortcutsUIButton(style: .darkOutline)
                                                      // shortcutTileColor: Color.green,
                                                      shortTitle: "Next Business Meeting",
                                                      systemImageName: "person.3")
                appshortcuts.append(nextMeetingShortcut)
            case .PrepareEvents:
                break
............  Etc.
             }
      }
      return appshortcuts
   }
}

Maybe using AppShortcutParameterPresentation? That is a parameter for one of the initializers for AppShortcut. Within AppShortcutParameterPresentation there is a parameter for an Options Collection. I haven't been able to figure out how to get that alternate init of AppShortcut working so please post the solution if you figure this out. I've been trying all day to get this to work with no luck.

https://developer.apple.com/documentation/appintents/appshortcut/init(intent:phrases:shorttitle:systemimagename:parameterpresentation:)