Siri is Apple’s personal virtual assistant which allows you to interact with your device via voice. SiriKit supports certain domains like VoIP calling, Messaging, Car Commands, Payments, Ride Booking, and Workouts. Within each domain, there is a series of intents, which are actions Siri can perform. For example, in Messaging domain, there are intents like sending a message, setting attributes on a message and searching for messages. In this blog, we are going to send a message without opening the app using Siri.
Let’s get started
First, create a simple view application.
Adding an intent extension to Xcode Project
Now go to File > New > Target . Then select Intents Extension from the list.


Enable Siri and App Groups
Go to the Capabilities of main app and enable Siri as follow


IntentHandler File
You can see the INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling protocol and its method implemented in IntentHandler file. In this demo, we will implement send message feature so here we need only INSendMessageIntentHandling. so you can remove the other two and its methods. We are going to make an app in which first Siri will ask about the contact to whom we want to send a message. After that Siri will ask about the message which we want to send. And then after approval, a message will be sent. First of all, we need to access Contact for the searching name in our contact list.
func searchContact(recipients : [INPerson], completionHandler: @escaping ([INPerson]?) -> Void) { let contactStore = CNContactStore() var finalContacts = [INPerson]() if recipients.count == 0 { completionHandler(nil) return } for objRecipient in recipients { do { let predicate = CNContact.predicateForContacts(matchingName: objRecipient.displayName) let filteredContacts = try contactStore.unifiedContacts(matching: predicate, keysToFetch: [CNContactFamilyNameKey as CNKeyDescriptor, CNContactMiddleNameKey as CNKeyDescriptor, CNContactGivenNameKey as CNKeyDescriptor, CNContactPhoneNumbersKey as CNKeyDescriptor]) for objContact in filteredContacts { if let objFilterContact = (recipients.filter{ ($0.displayName == objContact.givenName || $0.displayName == objContact.familyName || $0.displayName == objContact.middleName)}).first { finalContacts.append(objFilterContact) } } completionHandler(finalContacts) } catch { print("unable to fetch contacts") completionHandler(nil) } } }
func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INSendMessageRecipientResolutionResult]) -> Void) { if let recipients = intent.recipients { self.searchContact(recipients: recipients) { (finalRecipients) in // If no recipients were provided we'll need to prompt for a value. if finalRecipients == nil { completion([INSendMessageRecipientResolutionResult.needsValue()]) return } if finalRecipients?.count == 0 { completion([INSendMessageRecipientResolutionResult.needsValue()]) return } var resolutionResults = [INSendMessageRecipientResolutionResult]() // Implement your contact matching logic here to create an array of matching contacts switch finalRecipients!.count { case 2 ... Int.max: // We need Siri's help to ask user to pick one from the matches. if intent.recipients![0].siriMatches == nil { resolutionResults += [INSendMessageRecipientResolutionResult.success(with: intent.recipients![0])] } else { resolutionResults += [INSendMessageRecipientResolutionResult.disambiguation(with: finalRecipients!)] } case 1: // We have exactly one matching contact resolutionResults += [INSendMessageRecipientResolutionResult.success(with: finalRecipients![0])] case 0: // We have no contacts matching the description provided resolutionResults += [INSendMessageRecipientResolutionResult.unsupported()] default: break } completion(resolutionResults) } } }
Resolve Content Method
This method will be called when you speak your contact name. As per the above method, we search for the matching contact and depend on the matches found Siri will perform an action as per our requirement. -> If there are more than two matches found then Siri will ask about which contact you want to send a message. -> If there is one match found then Siri will ask about what message you want to send. -> Also if there is no match found then Siri will again ask about the contact you want to send a message. When you speak message then following method will be called.func resolveContent(for intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) { if let text = intent.content, !text.isEmpty { completion(INStringResolutionResult.success(with: text)) } else { completion(INStringResolutionResult.needsValue()) } }
func confirm(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) { // Verify user is authenticated and your app is ready to send a message. let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self)) let response = INSendMessageIntentResponse(code: .ready, userActivity: userActivity) completion(response) }
func handle(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) { // Implement your application logic to send a message here. let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self)) let response = INSendMessageIntentResponse(code: .success, userActivity: userActivity) let dictMessage = NSMutableDictionary() dictMessage.setObject(intent.content!, forKey: "message" as NSCopying) dictMessage.setObject(intent.recipients![0].displayName, forKey: "recipient" as NSCopying) UserDefaults(suiteName: "group.logistic.test")?.set(dictMessage, forKey: "dictMessage") completion(response) }

IntentViewController File
Below method will be called when Siri asked about the message and display the final message. so set your label’s value in this method.func configure(with interaction: INInteraction, context: INUIHostedViewContext, completion: @escaping (CGSize) -> Void) { let intent = interaction.intent as! INSendMessageIntent lblMessage.text = intent.content //configure your UI let size = CGSize.init(width: lblMessage.frame.width + 50, height: lblMessage.frame.height + 80) completion(size) }
func applicationDidBecomeActive(_ application: UIApplication) { if UserDefaults(suiteName: "group.logistic.test")?.object(forKey: "dictMessage") != nil { NotificationCenter.default.post(name: NSNotification.Name("MessageSent"), object: nil, userInfo: nil) } }
ViewController File
Take outlet of a message and recipient name label for displaying value. Then add and remove observer as below.override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.default.addObserver(self, selector: #selector(setMessageValue), name: NSNotification.Name(rawValue: "MessageSent"), object: nil) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self) }
@objc func setMessageValue() { if let dictMessage = UserDefaults(suiteName: "group.logistic.test")?.object(forKey: "dictMessage") { lblMsg.text = (dictMessage as! NSDictionary).object(forKey: "message") as? String lblRecipientName.text = (dictMessage as! NSDictionary).object(forKey: "recipient") as? String } }

Then stop the app and select MessageIntent as a target.





After this, Siri will ask about a message.



