Creating lists with SiriKit on iOS11

Last year, Apple announced SiriKit, which provides a lot of opportunities for the developers to provide functionality to the users via Siri. However, there are restrictions in the domains that can utilize this feature and if your app isn’t part of those domains, then you can’t make a big use of it. This year, Apple announced more domains, which means new opportunities for developers. There are updates in the Payments domain, meaning users can now send/transfer money between their accounts, pay bills and search through their accounts. New domain called visual codes is also introduced, which gives your app a chance to show meaningful visual code in the Siri context. This can be very handy in cases where your app stores digital tickets (for transport, cinemas, sport events etc), and when the user is nearby a place for validating the ticket, they can just say ‘Hey Siri, show my MovieTicketsApp ticket’ and Siri will ask your app to provide the ticket. Another new domain is Lists and Notes, which can be used for adding/removing items to a todo list, or adding notes. It’s really handy domain, which we will explore in more details in this post. We will create an app that will add/remove items to a grocery list.

In order to get started, you will need to grab an Xcode 9 (beta version if you are reading this before September) from Apple’s developer website. The UI of the main app will be really simple, with two screens. The first screen will show the lists that the user has created and when a list is selected, it will open a new screen with the tasks that it contains. Both screens will contain button for adding items (lists or tasks) to the table view:

1.png

The data in the main app will be synchronized with the Siri extension – whenever the user does an update (add/remove list/task) from Siri, the change should be reflected in the main app. So let’s first create the extension, you will need to create a new target called Intents extension:

2.png

Next, we will need to update the Info.plist of the Siri extension, so it knows which intents we are going to support in our app. Our app will support creation of lists, adding tasks to a list, as well as marking tasks as completed. In order to have this, we will need to add the INCreateTaskListIntent, INAddTasksIntent and INSetTaskAttributeIntent intents to our Info.plist file:

3.png

The next challenge we need to tackle is where to store the data the user created, so it’s accessible from both the main app and the Siri extension. Since we are storing the data only locally (we don’t have a backend), shared user defaults is a good option. In order to support this, you will need to create an app group, which then needs to be added to both targets. But in order to configure an app group, you will first need to create an app id from Apple’s developer portal:

4.png

Next, you will need to enable App Groups and Siri for the created app id:

5.png

Then, under identifiers, go to App Groups and create a new group id with a unique identifier and add this new group id to the app id (I know, a lot of ids :)):

6.png

If everything is setup correctly, you should see all the ticks under Capabilities -> App Groups, for both targets:

7.png

Now, we can load user defaults with the group id as a suite name and all the data between the app and the extension will be synced. We will create a new class, called ListsManager, which will handle all the changes in the user’s data – the creation and modification of the lists and tasks:

8.png

The functionality of the main app is just implementing the table view datasource and delegate methods and using the methods defined in the ListsManager, therefore it’s not that interesting. If you want to see the details, check the source code with the link provided at the end of this post.

We are more interested in the IntentHandler implementation in the SiriKit extension. As mentioned above, we will need to implement protocols from three intents INCreateTaskListIntent, INAddTasksIntent and INSetTaskAttributeIntent.

INCreateTaskListIntent

The first protocol we need to implement is for handling the creation of lists. As we know already, the steps required to implement SiriKit intent are confirm, resolve and handle (the first two being optional). For our use case, we don’t need them, so we will implement only the handle method. The handle method provides the intent, represented by the INCreateTaskListIntent object. This intent contains information about the title of the list that needs to be created, as well as any tasks that the user may have also provided in the request. We should handle this request by providing INCreateTaskListIntentResponse in the completion handler of the method. The INCreateTaskListIntentResponse also contains the information about the title and the tasks, as well some additional things like groupName, identifier and created/modifiedDateComponents, which are not needed in our case. In the implementation provided below, we are first taking the title of the intent and create the list using our ListsManager. Next, we check if there are any tasks that need to be added to the list. If there are, we are converting the strings provided by the intent to INTask objects and we are saving the strings to our ListsManager. If everything goes ok, we are sending a successful response to the completion handler:

9.png

Let’s test this. One cool addition to Xcode 9 is that you can test Siri with the simulator. Thanks Apple, no more voice straining and disturbing everyone around you. You can provide a sample text in the Siri scheme and that one will be executed when you run the extension. Click on the schemes button to the right of the Stop icon, then select Manage schemes and edit the Siri extension scheme. You will notice that in the Run action, there’s a new input field ‘Siri Intent Query’, which is the place where you should provide the text:

10.png

After you run the Siri extension, you should see a confirmation from Siri that the list has been created:

11.png

Also, open the main app and verify that it also created the grocery list there. And that’s all there is to creating lists with SiriKit. Pretty simple!

INAddTasksIntent

Next, let’s add some tasks to the list. In order to accomplish this, we need to implement the handle method of the INAddTasksIntentHandling protocol. Here we receive an INAddTasksIntent object, which similar to INCreateTaskListIntent, contains information about the task titles that need to be added, as well as the target list that will be modified. Apart from that, you can also provide spatial, which will display a reminder when the user approaches or leaves a specific location (or time, with a temporal event trigger). Similar to the creation of the list, we are first extracting the title of the list and then we are converting the task titles to INTask objects. We are adding the tasks to the ListsManager and then provide a successful response if everything went fine during the process:

12.png

If you run the extension with text like ‘Add milk, sugar and tomato in my grocery list in ListsSiriKit’, you will get the following Siri reply:

13.png

INSetTaskAttributeIntent

The last protocol we need to implement is INSetTaskAttributeIntentHandling from INSetTaskAttributeIntent. This protocol is used when we want to update the state of a task. A task in SiriKit can be in three states: unknown, notCompleted and completed. We want to show the task in the list when it’s in one of the first two states, but when the task is completed, we want to remove it from the list. To do this, we are extracting the title and status from the INSetTaskAttributeIntent. If the status is completed, we are just using the finish(task: title) method from our ListsManager, which goes through the saved tasks and deletes the completed task:

14.png

If you tell Siri: ‘Mark Sugar as completed in my grocery list in ListsSiriKit’, it will give you something like this:

15.png

And if you open the main app, you will notice that the ‘Sugar’ task is deleted from the grocery list, which is what we’ve expected to happen:

16.png

Conclusion

That wraps up this post. The new Lists API opens up a lot of possibilites with different kinds of lists and notes, so you can get pretty creative here. Have fun and also don’t forget to check the complete source code here.

20 Comments

  1. Hey ! Thanks for your article.
    I have one question about it.
    How would you do if the user asks to add an ingredient to a shopping list already available in the app but that was not created with INCreateTaskListIntent?
    Siri would display an error. Is there a way to tell Siri that a list already exists with a specific name?

    Like

    1. Hi, yes you can do that with App Groups as in the article. The data should be shared between the main iOS app and the app extension (Siri in this case), and you just need to check whether the item is in the shared user defaults.

      Take a look at the ListsManager and modify it for your needs to support that. After that, in Siri you just need to resolve the intent response with an error code.

      Hope that helps.

      Like

  2. Hello – thank you for the tutorial. It was very helpful. One thing I am running into is when my colleagues say “Hey Siri create grocery list in AppNameHere” – it doesn’t recognize our app name and instead creates the list in the reminders app. Any ideas on this? It works on my device but not others.

    Thank You

    Like

    1. Hi, thanks, glad you like it. Few things that come to my mind: check if people have opened at least once the main app and accepted the Siri permissions. Check in Settings->Siri if Siri is enabled and in App support whether your app is listed. Also, check whether the deployment version of the project and the Siri target, has to be smaller or equal than all testing devices. Other thing you might want to check, if you are testing in debug config and you are distributing in release config on TestFlight, whether correct entitlements are set. Hope that helps! Cheers, Martin

      Like

  3. Hello Martin, Thanks for the article. I want to register time and location based reminders in my app through Siri, I know that the Notes and Lists Domain have temporal and spatial event triggers but I’m having a hard time figuring how to use them.

    Like

    1. Hi Fahad, I haven’t tried time and location based reminders. From the documentation I can see that you have to provide that information to the INTask object, when you are doing the creation of the tasks. Check the createTasks(fromTitles taskTitles: [String]) here: https://github.com/martinmitrevski/ListsSiriKit/blob/master/Siri/IntentHandler.swift and provide the information in spatialEventTrigger and temporalEventTrigger. Hope that helps.

      Like

  4. Hello Martin,thanks for such nice article.I have two list in my app and both have ‘apple’ added.Now I want to delete apple from a particular list, how I can do that? Thanks

    Like

      1. Ya here deleting means marking complete(I am using it for removing in my app). Actually its not identifying the list.So that I can delete it from the local db.Hope you got my point.

        Like

  5. Thanks for the tutorial, I have a doubt. Is there any way to get selection event of the shown list in Siri kit.
    When I select the list item my app got open and I need to figure out which item user selected and I need to show different view controllers for different list items. Can you help me with this?

    Like

Leave a comment