SiriKit and Payment Domain; Part II
Intro
This is second article about SiriKit and Payment domain where we add custom UI for displaying accounts. First part is here. As well as source code which we use in this article. First part is about fetching account and displaying using build in UI. As for now we add our custom UI for account.
Let’s dive into Xcode and add custom UI for Siri (File > New > Target… > Intents UI Extension)
Name it as QuickBalanceIntentsUIExtension
and make sure to embed in main application.
Activate extension if prompted
Now we configure our extension to handle specific type of intents: INSearchForAccountsIntent
. To do that edit “Info.plist” for target
UI extension is quite simple an consist from one controller only. Let’s turn it in simple one account view. We add two labels for displaying account name and blanace and one UIImageView (with a custom chevron.righ system image)
Very important: make sure you have constraints defining view’s height. Otherwise it will be calculated as 0 height view.
Now let’s add some code in IntentViewController.swift
. The only function we really should pay attention is
optional func configureView(for parameters: Set<INParameter>, of interaction: INInteraction, interactiveBehavior: INUIInteractiveBehavior, context: INUIHostedViewContext, completion: @escaping (Bool, Set<INParameter>, CGSize) -> Void)
This fucntion will be called for all the parameters. You might find a lot of articles online with messenger example, or custom “book a ride” example, or custom payment example. All of them perfectly cover that this function is called for parameters like “from”, “to”, “message”, “amount”, etc. But key thing here for us is to understand that “parameter” is an account. So our function will be called as many times as much accounts we have. There is also one tricky case when you ask Siri for one specific account and not list of them. But will cover it in next article. For now, replace function’s body with
func configureView(for parameters: Set<INParameter>, of interaction: INInteraction, interactiveBehavior: INUIInteractiveBehavior, context: INUIHostedViewContext, completion: @escaping (Bool, Set<INParameter>, CGSize) -> Void) { /// 01 guard let _ = interaction.intentResponse as? INSearchForAccountsIntentResponse else { completion(false, parameters, CGSize.zero) return } /// 02 guard parameters.count > 0 else { completion(true, parameters, CGSize.zero) return } /// 03 parameters.forEach { (parameter) in if let account = interaction.parameterValue(for: parameter) as? INPaymentAccount { accountTitleLabel.text = account.nickname?.spokenPhrase accountBalanceLabel.text = account.balance?.amount!.floatValue.description } }let viewSize = view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) completion(true, parameters, viewSize)}
The code is as simple and as minimalistic as it can be here. First, we check if we really serve INSearchForAccountsIntent
type of intent (01). Second, make sure you have more then zero parameters (02).That should never be the case, yet documentation and other articles suggest to perform this check. Third, is most awkward piece of code. Sadly I wasn't able to find anything neither in official Apple’s documentation, nor in any tutorial online. So, this is “my” version of working with accounts. Not claim it’s correct. The idea is that parameters only contains key-path of parameter which now we configure UI for. To explain what I mean, stop in debugger and print parameters
Printing description of parameters:
▿ 1 element
- 0 : <INParameter: 0x2813faea0> {
parameterClass = INSearchForAccountsIntentResponse;
parameterKeyPath (subscripted) = accounts[0];
}
Yet interaction
contains value for parameter. So we assume it is INPaymentAccount
and try to cast (03).
That’s all for coding. Let’s add sample phrases how user can interact with our app. This is Apple’s requirement and you get warning when submit app to App Store if not fulfilled it. Basically, it simple property file with very specific name “AppIntentVocabulary.plist” containing example phrases for our type on intent (INSearchForAccountsIntent)
Let’s run and check. There is again a bit tricky part with running. I can’t launch it properly on simulator with Xcode 11.3.1. Yet, I was able to do that on previous version. Apparently Apple changing something. May work again in future. But for now I use real device. Second thing to understand is that you cant launch QuickBalanceIntentsUIExtension target. It should be QuickBalanceIntentsExtension (with no UI in it). But to deliver the actual UI part you need to build and launch either main app (MyBank target) or UI extension (QuickBalanceIntentsUIExtension target) and only after it launch Siri — QuickBalanceIntentsExtension.