Siri shortcuts is a big headline feature Apple introduced with iOS 12. It allows users to add custom phrases to Siri for specific actions offered by apps. These shortcut actions can be offered by the app to the users in multiple ways. One important way is to show a button inside the app.
You can design a custom button but Apple encourages you to use the official INUIAddVoiceShortcutButton
. You can define its appearance (black or white) and it offers some pretty nice functionality if it is used correctly.
The official documentation for INUIAddVoiceShortcutButton
offers some example code:
// Add an "Add to Siri" button to a view.
func addSiriButton(to view: UIView) {
let button = INUIAddVoiceShortcutButton(style: .blackOutline)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
view.centerXAnchor.constraint(equalTo: button.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: button.centerYAnchor).isActive = true
button.addTarget(self, action: #selector(addToSiri(_:)), for: .touchUpInside)
}
// Present the Add Shortcut view controller after the
// user taps the "Add to Siri" button.
@objc
func addToSiri(_ sender: Any) {
if let shortcut = INShortcut(intent: orderSoupOfTheDayIntent) {
let viewController = INUIAddVoiceShortcutViewController(shortcut: shortcut)
viewController.modalPresentationStyle = .formSheet
viewController.delegate = self // Object conforming to `INUIAddVoiceShortcutViewControllerDelegate`.
present(viewController, animated: true, completion: nil)
}
}
However this example code does not offer a good user experience!
- The button does not automatically change its UI when the shortcut has already been added by the user
- Tapping the button does not allow to edit the shortcut when it has been created already
This can be done a lot better. Unfortunately Apple does not explain this in their documentation.
The key to a better user experience is INUIAddVoiceShortcutButtonDelegate. It allows you to present either INUIAddVoiceShortcutViewController
or INUIEditVoiceShortcutViewController
depending on whether the user has already added a custom phrase to this shortcut.
The delegate can be implemented like this:
extension MyViewController: INUIAddVoiceShortcutButtonDelegate {
func present(_ addVoiceShortcutViewController: INUIAddVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) {
addVoiceShortcutViewController.delegate = self
addVoiceShortcutViewController.modalPresentationStyle = .formSheet
present(addVoiceShortcutViewController, animated: true, completion: nil)
}
func present(_ editVoiceShortcutViewController: INUIEditVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) {
editVoiceShortcutViewController.delegate = self
editVoiceShortcutViewController.modalPresentationStyle = .formSheet
present(editVoiceShortcutViewController, animated: true, completion: nil)
}
}
Do not forget to assign the INUIAddVoiceShortcutButtonDelegate
after creating the button:
let button = INUIAddVoiceShortcutButton(style: .black)
button.translatesAutoresizingMaskIntoConstraints = false
button.shortcut = INShortcut(intent: WarningLevelIntent())!
button.delegate = self
// then add the button as a subview and create constraints to place it correctly
You also need to set button.shortcut
to the shortcut you want to be managed by this button. This will automatically update the button to display a different UI when the user has already added a custom phrase to the shortcut. When the button is tapped the correct INUIAddVoiceShortcutButtonDelegate
function is called to either allow assigning the custom phrase or to edit the existing one.
The only thing that’s still missing is the implementation of the INUIAddVoiceShortcutViewControllerDelegate
and INUIEditVoiceShortcutViewControllerDelegate
. This is a very minimal implementation and you can extend this to your needs:
extension MyViewController: INUIAddVoiceShortcutViewControllerDelegate {
func addVoiceShortcutViewController(_ controller: INUIAddVoiceShortcutViewController, didFinishWith voiceShortcut: INVoiceShortcut?, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
func addVoiceShortcutViewControllerDidCancel(_ controller: INUIAddVoiceShortcutViewController) {
controller.dismiss(animated: true, completion: nil)
}
}
extension MyViewController: INUIEditVoiceShortcutViewControllerDelegate {
func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didUpdate voiceShortcut: INVoiceShortcut?, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didDeleteVoiceShortcutWithIdentifier deletedVoiceShortcutIdentifier: UUID) {
controller.dismiss(animated: true, completion: nil)
}
func editVoiceShortcutViewControllerDidCancel(_ controller: INUIEditVoiceShortcutViewController) {
controller.dismiss(animated: true, completion: nil)
}
}
I hope this helps someone trying to add a INUIAddVoiceShortcutButton
correctly to his app. When I tried to implement this I could not find any good documentation on how to do this. This is why I decided to write this little blog post for you.