How to add AppIntent to app target and use it in an interactive widget
Asked Answered
F

5

13

How do you create an AppIntent that will run in your app's process and use it in your widget's Button? I created a new file, added it to my app's target, created my AppIntent struct, then added a button to my widget: Button(intent: TestIntent()) { Text("Test") }. This results in a compile-time error Cannot find 'TestIntent' in scope which makes sense because this code doesn't exist in the widget extension where I'm trying to use it. But the documentation for adding interactivity to widgets states:

For a widget, create a new structure that adopts the AppIntent protocol and add it to your app target ... In the protocol’s required perform() function, add code for the action you want to make available to the widget.

and

If you adopt the AppIntent protocol, you can add your custom app intent to your widget extension target or your app target. Adding it to the app target is preferable because you can then reuse the button or toggle that you add to your widget in your app.

and

When a person interacts with a button or toggle in your widget, the system runs the perform() function in your app’s process.

Frolick answered 10/7, 2023 at 18:36 Comment(0)
F
12

If you add the AppIntent only to your widget extension target, the intent will always run in the widget process.

If you add the AppIntent to both your app and widget extension targets, the intent will run in your app process if your app is running (even if suspended in the background) and in the widget process when your app is not running. If you want it to always run in your app's process, you can implement openAppWhenRun which will cause your app to come to foreground, or you implement a different intent protocol that runs in the app's process in the background: AudioPlaybackIntent, LiveActivityIntent, or ForegroundContinuableIntent.

Note the documentation has been updated to address some previous inaccuracies. And thanks to Michael on Mastodon for clarifying the behavior!

In my app, in order to get it to always run in the main app process, I created a regular AppIntent, added that file to both the app and widget targets, then conformed it to ForegroundContinuableIntent but only for the main app not the extension. ForegroundContinuableIntent is unavailable in application extensions for iOS, so you have to annotate it like so:

struct MyWidgetButtonIntent: AppIntent {
    // ...
}

@available(iOSApplicationExtension, unavailable)
extension MyWidgetButtonIntent: ForegroundContinuableIntent { }
Frolick answered 12/7, 2023 at 18:50 Comment(2)
Do you know if this AppIntent is then also available through the Shortcuts app and Siri voice commands? I am looking for a way to have an AppIntent that runs code of my main app and also in the main app process, as it starts GPS tracking.Backed
@Backed Shortcuts should find any AppIntent in your main app, may want to add it in your App Shortcuts though to appear in Spotlight etcFrolick
K
2

I found a better solution for interaction Live Activity.

  1. You can create a struct impletion LiveActivityIntent then add it to widget target and app target.
  2. Create two files, one add to widget target, other one add to app target, each extension your Intent and write one same function. Widget target function can empty, app target function is real code. In perform call your function in extensions file.

Above steps make sure compile success and when interaction intent, System will execute your real code in App Process.

Kasi answered 23/8, 2023 at 3:55 Comment(2)
Can you give a code example?Merline
iOS 18 beta, it looks like brokenOracle
P
1

I was experiencing the same thing. I'm guessing it's just a bug with the Betas, because it does seem to contradict the documentation.

I found a workaround (as of Xcode beta 5, iOS17 beta 4). If you implement AudioPlaybackIntent instead of AppIntent, then it always launches inside the App's process.

Percival answered 25/7, 2023 at 23:22 Comment(0)
G
1

We can try AppIntentsPackage API

  1. create a MyIntent framework

  2. define a TestIntent

    // MyIntent.framework
    public struct TestIntent: AppIntent {
     static var title: LocalizedStringResource = "xxx"
    
    
     func perform() async throws -> some IntentResult {
         ...
         return .result()
     }
    }
    
    public struct TestIntents: AppIntentsPackage {}
    
  3. import the MyIntent.framework in App or Widget Target.

Garble answered 26/7, 2023 at 4:35 Comment(0)
R
1

As Version 15.0 beta 7, a LiveActivityIntent runs in the main app process as expected.

Rooney answered 27/8, 2023 at 13:51 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.