Xamarin.iOS - Soup Chef

This is a Xamarin port of Apple's Soup Chef sample. It demonstrates how to create an app that makes use of Siri Shortcuts, introduced in iOS 12.

The application provides a way to order soup from a fictitious soup restaurant, view your order history, and assign voice shortcuts to orders. These voice shortcuts can then be invoked with Siri, making it possible to order soup without even opening the app.

Usage

To use the app, set up an order and assign it a voice shortcut:

  • Open the app.
  • Click the + button in the upper-right, and create a new soup order.
  • After creating the order, it will appear on the Order History screen in the app. Select the order, then tap Add to Siri on the bottom of the screen.
  • Record a phrase to use to make the same order again in the future with Siri. For example, for an order that contains two bowls of chowder with cheese, you might specify, "Order two bowls of chowder with cheese."

Then, order soup with Siri:

  • Minimize or close the Soup Chef app.
  • Invoke Siri in whatever way you prefer, and issue the voice command you defined above. For example, "Hey Siri, order two bowls of chowder with cheese." Siri will interact with you to create and execute the order.

Repository overview

This repository contains the following folders:

  1. OrderSoupIntentCodeGen/

    This folder contains an Xcode project that includes the Intents.intentdefinition file that defines the custom intent file used by the Soup Chef app. This file is the basis for custom interactions with Siri, and Xcode generates some code based on its contents (which can be edited with Xcode). The Xamarin.iOS SoupChef project relies on a C# binding to this generated code.

  2. OrderSoupIntentStaticLib/

    This folder contains an Xcode project used to create a static library from the code generated by the OrderSoupIntentCodeGen project. It also contains a Makefile to generate C# bindings definitions (ApiDefinitions.cs and StructsAndEnums.cs files) for this static library, based on the .h and .m files created by Xcode.

  3. OrderSoupIntentBinding/

    This project takes the C# bindings definitions and static library created by the OrderSoupIntentStaticLib project and uses them to create an assembly for use in a Xamarin.iOS application. There's no reason to open this project directly, as it is included in the SoupChef solution.

  4. SoupChefIntents/

    The Soup Chef Intents Extension, which performs the background work necessary to order soup when requested by Siri.

  5. SoupChefIntentsUI/

    The Soup Chef Intents UI Extension, which contains the custom UI used to interact with Siri when ordering soup.

  6. SoupKit/

    Shared code

  7. SoupChef/

    The Xamarin.iOS Soup Chef app. This solution contains references to these other projects:

    • OrderSoupIntentBinding
    • SoupChefIntents
    • SoupChefIntentsUI
    • SoupKit

Building the static library and C# bindings definitions

The SoupChef app relies on the OrderSoupIntentBinding project, which in turn relies on the static library produced by OrderSoupIntentStaticLib and its associated C# bindings definitions.

Follow these instructions to make sure that the static library and C# bindings definitions build as expected:

  • Install Objective Sharpie, the tool used to generate bindings definitions from the .h and .m files created by Xcode.
  • Configure your system to use Xcode 10 Command Line Tools:
    • WARNING: Updating the selected Command Line Tools impacts all installed versions of Xcode on your system. When you are done using the Soup Chef sample app, be sure to revert this setting to its original configuration.
    • In Xcode, choose Xcode > Preferences > Locations and set Command Line Tools to the most current Xcode 10 installation available on your system.
  • In the terminal, cd to the OrderSoupIntentStaticLib directory.
  • Type make, which builds:
    • The static library, libOrderSoupIntentStaticLib.a
    • In the bo output directory, C# bindings definitions:
      • ApiDefinitions.cs
      • StructsAndEnums.cs

The OrderSoupIntentBindings project, which relies on this static library and its associated bindings definitions, builds these items automatically. However, manually running through the above process will ensure that the static library and bindings definitions build as expected.

Running the app on simulator or device

It can be helpful when working with Siri Shortcuts to enable two settings on your iOS device or simulator:

  • In the Settings app, enable Developer > Display Recent Shortcuts.
  • In the Settings app, enable Developer > Display Donations on Lock Screen.

These settings will surface recently created shortcuts on the lock screen and in the search results (accessible by swiping down on the iOS home screen).

Running on simulator

This app works on the simulator. On the Hardware menu of macOS Simulator application (the iOS simulator), select Siri to invoke Siri, and issue commands with your voice just as you would with a physical device. If you run the app on simulator, you will not need to to through the provisioning steps described below in the Running on device section.

Running on device

To run this application on a device, you'll need to set up an App Group, some App IDs, and some development provisioning profiles; make a few edits to Info.plist, Entitlements.plist, and one source code file; and configure the build settings for each project to use the correct provisioning profiles.

Please also see the note about automatic provisioning, below.

App Group, App IDs, Provisioning Profiles

In the Certificates, IDs & Profiles section of the Apple Developer Portal, do the following:

  • Create an App Group to share data between the SoupChef app and its extensions. For example: group.com.yourcompanyname.SoupChef

  • Create three App IDs: one for the app itself, one for the Intents Extension, and one for the Intents UI Extension. For example:

    • App: com.yourcompanyname.SoupChef

      • To this App ID, assign the SiriKit and App Groups capabilities.
    • Intents extension: com.yourcompanyname.SoupChef.Intents

      • To this App ID, assign the App Groups capability.
    • Intents UI extension: com.yourcompanyname.SoupChef.Intentsui

      • This App ID needs no special capabilities.
  • After creating the the above App IDs, edit the App Groups capability assigned to the app and the Intents extension, specifying the specific App Group created above.

  • Create three new development provisioning profiles, one for each of the new App IDs.

  • Download these provisioning profiles and double-click each one to install it. If Visual Studio for Mac or Visual Studio 2017 is already running, restart it to make sure it registers the new provisioning profiles.

Editing Info.plist, Entitlements.plist, and source code

In Visual Studio for Mac or Visual Studio 2017, do the following:

  • Update the various Info.plist files in the solution. Set the app, Intents Extension, and Intents UI extension Bundle Identifier to the App IDs defined above:

    • App: com.yourcompanyname.SoupChef
    • Intents Extension: com.yourcompanyname.SoupChef.Intents
    • Intents UI Extension: com.yourcompanyname.SoupChef.Intentsui
  • Update the Entitlements.plist file for the SoupChef project:

    • For the App Groups capability, set the group to the new App Group created above (in the example above, it was group.com.yourcompanyname.SoupChef).
    • Make sure that SiriKit is enabled.
  • Update the Entitlements.plist file for the SoupChefIntents project:

    • For the App Groups capability, set the group to the new App Group created above (in the example above, it was group.com.yourcompanyname.SoupChef).
  • Finally, open NSUserDefaultsHelper.cs. Set the AppGroup variable to the value of your new App Group (for example, set it to group.com.yourcompanyname.SoupChef).

Configuring the build settings

In Visual Studio for Mac or Visual Studio 2017:

  • Open the options/properties for the SoupChef project. On the iOS Bundle Signing tab, set Signing Identity to automatic and Provisioning Profile to the new app-specific provisioning profile you created above.

  • Open the options/properties for the SoupChefIntents project. On the iOS Bundle Signing tab, set Signing Identity to automatic and Provisioning Profile to the new intents project-specific provisioning profile you created above.

  • Open the options/properties for the SoupChefIntentsUI project. On the iOS Bundle Signing tab, set Signing Identity to automatic and Provisioning Profile to the new intents UI-specific provisioning profile you created above.

With these changes in place, the app will run on an iOS device.

Automatic provisioning

Note that you can use automatic provisioning to accomplish many of these provisioning tasks directly in the IDE. However, automatic provisioning does not set up App Groups. You will need to manually configure the Entitlements.plist files with the name of the App Group you would like to use, visit the Apple Developer Portal to create the App Group, assign that App Group to each App ID created by automatic provisioning, regenerate the provisioning profiles (app, Intents Extension, Intents UI Extension) to include the newly created App Group, and download and install them.

Notes

  • The OrderDetail.storyboard file makes use of secondary views, which Xamarin's iOS Designer cannot edit. To edit these, use Xcode's Interface Builder.
  • After telling Siri to make a soup order or tapping a shortcut in a search result or on the lock screen, there can be a long lag before the user interface appears. This appears to be consistent with the Swift-based app.
  • The application console occasionally shows an errors such as: "Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd". This seems to be innocuous.
  • The application console occasionally indicates "Unable to find cacheable object with identifier intents-remote-image-proxy." This is consistent with the Swift-based app.

Known Issues (TO FIX)

  • After successfully completing an order made via voice, Siri does not report back the success message defined by the Intents.intentdefinition file.
  • There is a persistent warning in the application console: "Warning: observer object was not disposed manually with Dispose()"

License

Xamarin port changes are released under the MIT license.