Connecting Siri to Custom Intents in SwiftUI

Making a custom intent for Siri is pretty simple and there are some good articles on it out there but when I followed them it wasn’t working for me. Specifically asking Siri or invoking a shortcut was returning an error and my Intent handler wasn’t being called. The answer turned out to be really simple once I knew what I was doing. Hopefully this helps someone else.

Creating the intent

For SwiftUI this article is a good walk through, the Apple documentation is also good. What was missing for me is how Siri knows to invoke your extension in a SwiftUI app. For a normal app you do this according to the Apple docs: “In an iOS app or an app built with Mac Catalyst, implement application(_:handlerFor:) on your UIApplicationDelegate.” For SwiftUI apparently there are equivalent methods, see this discussion. However, I didn’t have to do any of that to make it work. I created the intent definition and the Intents extension as per the example above, then there was just one more step to put it all together.

Implement the interface

The Intents extension creates an IntentHandler class, that class has to implement the interface of the Intent definition you created. The hidden part is that the Intent definition automatically generates several protocols that you must implement. For example, let’s say you create an Intent definition called MyCustom. That will generate the MyCustomIntentHandling protocol. If you implement that protocol on the IntentHandler class then through some behind the scenes magic SwiftUI will invoke your IntentHandler.

class IntentHandler: INExtension, MyCustomIntentHandling {

    func handle(intent: MyCustomIntent, completion: @escaping (MyCustomIntentResponse) -> Void) {
    completion(MyCustomIntentResponse(code: MyCustomIntentResponseCode.success, userActivity: nil ))
  }
    
  override func handler(for intent: INIntent) -> Any {
        // This is the default implementation.  If you want different objects to handle different intents,
        // you can override this and return the handler you want for that particular intent.

        return self
  }
    
}

The specific methods you have to implement will vary depending on what the MyCustomIntentHandling protocol defines. In Xcode you should get help with the right message signatures. If you don’t, or it won’t auto-complete the name of MyCustomIntentHandling then go back to your intent definition and make sure you have the right name. With that done Siri and actions should start to invoke your extension code.

What’s missing from many of the examples I’ve seen is that they rely on the automatic implementations of some standard protocols like INSendMessageIntentHandling which is already implemented on the IntentHandler when Xcode generates it. So they don’t make it explicit that if you are creating a custom intent you need to do this step manually.

Leave a Reply