SiriKit For Payments – Access Account Balance
This article demoes how to connect your iOS financial app to Siri so that user can ask for the account balance information with their voice.
OverView:
SiriKit supports user interaction with the app in 11 domains (as of today) like Messaging, Workouts, Payments, CarPlay, Photos, VOIP calling etc.
The type of requests user can make are categorized into intents and related intents are grouped into domains.
All the user requests with Siri are communicated to the intent App extensions. Intents should be to make API calls on service frameworks and respond back to user. Below picture the idea that we are going to develop in this article.
Since Siri communication is interactive, it’s not necessary that intent gets all the necessary details of the request at first go. For Example, if you ask Siri “what is account balance?, we still need more information about which account balance user is expecting, So Siri should be able to question back what type of account user is looking for. When user responds with account type as Checking or Saving account, intent should be in a position to handle the request or ask back Siri for any clarification, confirmation.
To search for balance, SiriKit provides us with INSearchForAccountsIntent class and we need to add functionality to adopt to INSearchForAccountsIntentHandling protocol.
Adopting INSearchForAccountsIntentHandling requires handling methods to cover 3 below topics.
- Handling the Intent – Called when it is time to search for the account information.
- Confirming the Response – Called when it is time for you to confirm whether you can perform the search.
- Resolving Details of the Intent – Called when you need for clarification to complete the request.
Getting Started:
Create SiriKitDemo App –
Let’s create a simple iOS Single View App and name it SiriKitDemo. Create UILabels for showing amount balance for both Checking and Saving accounts. Add IBOutlets as shown below.
In info.plist, add key “Privacy – Siri Usage Description” with the description to take user permission to access Siri.
Working on SiriKit requires Siri capability enabled. First create App ID on Apple developer site using the same Bundle Id you see in Xcode. Ensure that “SiriKit” service is selected.
Now in Xcode, go to project target (SiriKitDemo), capabilities tab and enable Siri option. If you don’t see Siri option, close Xcode and reopen and you should Siri option.
Create App service Framework –
Now, lets create Payments Service framework that can make Service API calls to fetch account balance info. Create a new target and select Cocoa Touch Framework template and name it PaymentsFramework.
Now add new Swift File “AccountBalance.swift” and create a singleton class AccountsBalance. This class is responsible is for making service calls to fetch the balance amount.
Here, I created an enum AccountType and created a service method getAccountBalance that can take account type as parameter and return the amount. I am just returning the hardcoded amounts but in real projects, this is where service calls are made and amount is returned in completion handler.
public enum AccountType {
case checking
case savings
}
public final class AccountsBalance {
private var checkingBalance: Double = 0
private var savingsBalance: Double = 0
public static var shared: AccountsBalance = AccountsBalance()
private init() {}
// get the balance info from API service.
public func getAccountBalance(type: AccountType, completionHandler: (Double) -> ()) {
let balance: Double = type == .checking ? 2000: 500
completionHandler(balance)
}
}
Now, let’s use getAccountBalance API in the app. Go to ViewController.Swift in SiriKitDemo project. Import the just created framework with “import PaymentsFramework”. Create loadBalance() function and call it in ViewDidLoad(). Here, we call the API from the framework and populate the UILabels for Checking and Savings. Run the app to ensure you see fetched amounts.
private func loadBalance() {
AccountsBalance.shared.getAccountBalance(type: AccountType.checking) { (amount) in
DispatchQueue.main.async {
self.checkingBalanceLabel.text = String(amount)
}
}
AccountsBalance.shared.getAccountBalance(type: AccountType.savings) { (amount) in
DispatchQueue.main.async {
self.savingsBalanceLabel.text = String(amount)
}
}
}
Create Intent Extension:
Add new target and select Intents Extension template and name it PaymentsIntents. Open the IntentHandler.swift and see you all the sample code provided for messaging domain. We don’t need any of that code.
In PaymentsIntents info.plist, add INSearchForMessagesIntent entry as shown below to let Siri aware of the intents the app uses.
Handle Siri Requests:
Create new swift file in Intent extension and name it SearchBalanceIntentHandler.swift. Create a class SearchBalanceIntentHandler that extends INSearchForAccountsIntentHandling and NSObject protocol. This class is responsible for handling and resolving the voice requests. Also import PaymentsFramework.
import Intents
import PaymentsFramework
class SearchBalanceIntentHandler: NSObject, INSearchForAccountsIntentHandling {
func handle(intent: INSearchForAccountsIntent, completion: @escaping (INSearchForAccountsIntentResponse) -> Void) {
}
func resolveAccountType(for intent: INSearchForAccountsIntent, with completion: @escaping (INAccountTypeResolutionResult) -> Void) {
}
}
Let’s add code to complete handle(intent:completion:) and resolveAccountType(for:with:) methods.
resolveAccountType(for:with:) – When user makes a request for account balance, Intent calls resolveAccountType(for:with:) method passing the request information in intent parameter. It is up to us to check intent account type and callback completion handler.
func resolveAccountType(for intent: INSearchForAccountsIntent, with completion: @escaping (INAccountTypeResolutionResult) -> Void) {
if intent.accountType == .checking {
completion(INAccountTypeResolutionResult.success(with: intent.accountType))
} else if intent.accountType == .saving {
completion(INAccountTypeResolutionResult.success(with: intent.accountType))
} else {
completion(INAccountTypeResolutionResult.needsValue())
}
}
In resolveAccountType method, intent object has property accountType of type INAccountType enum. It can be one of checking, credit, debit, investment, mortgage, prepaid, saving, unknown type. If user asks say something like “show my checking balance” or ”show my saving balance”, we have all the necessary information and call completion handler with resolution result as success with account type. Otherwise, we pass resolution result as needsvalue so that Siri can ask back user for the type of account for which balance is needed.
When we call resolveAccountType completion with INAccountTypeResolutionResult.success, control goes to handle(intent:completion:) to process the request and send back the result to the user.
handle(intent:completion:) – In this method, we make service API call to fetch the account balance and callback the completion handler with the service status INSearchForAccountsIntentResponse response. If successful, we send the balance amount in INBalanceAmount object.
func handle(intent: INSearchForAccountsIntent, completion: @escaping (INSearchForAccountsIntentResponse) -> Void) {
let type: AccountType = (intent.accountType == .checking) ? AccountType.checking : AccountType.savings
AccountsBalance.shared.getAccountBalance(type: type) { (amount) in
let res = INSearchForAccountsIntentResponse(code: INSearchForAccountsIntentResponseCode.success, userActivity: nil)
let balanceAmount = INBalanceAmount(amount: NSDecimalNumber(value: amount), currencyCode: "USD")
if intent.accountType == .checking {
let paymentAccount = INPaymentAccount(nickname: INSpeakableString.init(spokenPhrase: "amount"), number: nil, accountType: intent.accountType, organizationName: nil, balance: balanceAmount, secondaryBalance: nil)
res.accounts = [paymentAccount]
completion(res)
} else if intent.accountType == .saving {
let paymentAccount = INPaymentAccount(nickname: INSpeakableString.init(spokenPhrase: "amount"), number: nil, accountType: intent.accountType, organizationName: nil, balance: balanceAmount, secondaryBalance: nil)
res.accounts = [paymentAccount]
completion(res)
} else {
completion(INSearchForAccountsIntentResponse(code: INSearchForAccountsIntentResponseCode.failure, userActivity: nil))
}
}
}
Below is summary of functionality in handle(intent:completion:) method:
- Check for intent.accountType to see if it either Checking or Saving.
- Make service call to fetch the amount by passing the account type as parameter AccountsBalance.shared.getAccountBalance(type: type) { (amount) in {}
- If service call failed, we send the response fail completion handler
completion(INSearchForAccountsIntentResponse(code: INSearchForAccountsIntentResponseCode.failure, userActivity: nil)) - If service call is successful and received the amount.
- Create INBalanceAmount object with the balance amount with its currency code.
- Create INPaymentAccount object with INBalanceAmount as parameter along with INSpeakableString which is displayed to user in Account column.
Now go to PaymentsIntent target, Build Phases and ensure you see SearchBalanceIntentHandler.swift in the compiler sources. If not there, just add it by clicking the plus button and selecting the file.
Last thing to do is to go to IntentHandler class and return SearchBalanceIntentHandler instance if the intent is of INSearchForAccountsIntent
override func handler(for intent: INIntent) -> Any {
if intent is INSearchForAccountsIntent {
return SearchBalanceIntentHandler()
}
return self
}
Now, we are done with the coding. Below is how to test it.
- On Device – Install the app on to device, go to Siri and ask for a question like “What is checking accounts balance”.
- On Simulator – Go to PaymentsIntent Edit scheme, add the query “What is checking accounts balance” in Siri Intent Query and select “Ask on Launch” in Executable.
Here is the working SiriKitDemo output.
For any questions, please feel free to reach out. I will be glad to answer.
For any questions, please feel free to reach out. I will be glad to answer.
For any questions, please feel free to reach out. I will be glad to answer.

















