We have an app with carplay-messaging capability, and have successfully integrated our app in order to read out the list of unread messages.
However, if a single message arrives and our push notification appears, we are not able to have the Siri UI automatically read only that single message or announce it.
The 'list' UI appears (siri: "would you like to read your messages...") when tapping the notification, whereas we would like the 'item' UI to appear immediately with the "reply, repeat, don't reply" buttons.
Our intent handler service looks like this - basically the auto-generated one for a new Intent Handler Extension:
import Intents
// As an example, this class is set up to handle Message intents.
// You will want to replace this or add other intents as appropriate.
// The intents you wish to handle must be declared in the extension's Info.plist.
// You can test your example integration by saying things to Siri like:
// "Send a message using <myApp>"
// "<myApp> John saying hello"
// "Search for messages in <myApp>"
class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling {
override func handler(for intent: INIntent) -> Any {
// This is the default implementation. If you want different objects to handle different intents,
// you can override this and return the handler you want for that particular intent.
return self
}
// MARK: - INSendMessageIntentHandling
// Implement resolution methods to provide additional information about your intent (optional).
func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INSendMessageRecipientResolutionResult]) -> Void) {
if let recipients = intent.recipients {
// If no recipients were provided we'll need to prompt for a value.
if recipients.count == 0 {
completion([INSendMessageRecipientResolutionResult.needsValue()])
return
}
var resolutionResults = [INSendMessageRecipientResolutionResult]()
for recipient in recipients {
let matchingContacts = [recipient] // Implement your contact matching logic here to create an array of matching contacts
switch matchingContacts.count {
case 2 ... Int.max:
// We need Siri's help to ask user to pick one from the matches.
resolutionResults += [INSendMessageRecipientResolutionResult.disambiguation(with: matchingContacts)]
case 1:
// We have exactly one matching contact
resolutionResults += [INSendMessageRecipientResolutionResult.success(with: recipient)]
case 0:
// We have no contacts matching the description provided
resolutionResults += [INSendMessageRecipientResolutionResult.unsupported()]
default:
break
}
}
completion(resolutionResults)
} else {
completion([INSendMessageRecipientResolutionResult.needsValue()])
}
}
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())
}
}
// Once resolution is completed, perform validation on the intent and provide confirmation (optional).
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)
}
// Handle the completed intent (required).
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)
completion(response)
}
// Implement handlers for each intent you wish to handle. As an example for messages, you may wish to also handle searchForMessages and setMessageAttributes.
// MARK: - INSearchForMessagesIntentHandling
func handle(intent: INSearchForMessagesIntent, completion: @escaping (INSearchForMessagesIntentResponse) -> Void) {
// Implement your application logic to find a message that matches the information in the intent.
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSearchForMessagesIntent.self))
let response = INSearchForMessagesIntentResponse(code: .success, userActivity: userActivity)
// Initialize with found message's attributes
response.messages = [INMessage(
identifier: "identifier",
conversationIdentifier: "convo1",
content: "I am so excited about SiriKit!",
dateSent: Date(),
sender: INPerson(personHandle: INPersonHandle(value: "sarah@example.com", type: .emailAddress), nameComponents: nil, displayName: "Sarah", image: nil, contactIdentifier: nil, customIdentifier: nil),
recipients: [INPerson(personHandle: INPersonHandle(value: "+1-415-555-5555", type: .phoneNumber), nameComponents: nil, displayName: "John", image: nil, contactIdentifier: nil, customIdentifier: nil)],
messageType: .text
)]
completion(response)
}
// MARK: - INSetMessageAttributeIntentHandling
func handle(intent: INSetMessageAttributeIntent, completion: @escaping (INSetMessageAttributeIntentResponse) -> Void) {
// Implement your application logic to set the message attribute here.
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSetMessageAttributeIntent.self))
let response = INSetMessageAttributeIntentResponse(code: .success, userActivity: userActivity)
completion(response)
}
}
Is there specific configuration required to allow display of a single message via tapping on the notification?
SiriKit
RSS for tagHandle requests for your app’s services from users using Siri or Maps.
Posts under SiriKit tag
43 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
guard let fileURL = intent.attachments?.first?.audioMessageFile?.fileURL else {
print("Couldn't get fileNameWithExtension from intent.attachments?.first?.audioMessageFile?.fileURL?.lastPathComponent")
return failureResponse
}
defer {
fileURL.stopAccessingSecurityScopedResource()
}
let fileURLAccess = fileURL.startAccessingSecurityScopedResource()
print("FileURL: \(fileURLAccess)")
let tempDirectory = FileManager.default.temporaryDirectory
let tempFileURL = tempDirectory.appendingPathComponent(UUID().uuidString + "_" + fileURL.lastPathComponent)
do {
// Check if the file exists at the provided URL
guard FileManager.default.fileExists(atPath: fileURL.path) else {
print("Audio file does not exist at \(fileURL)")
return failureResponse
}
fileURL.stopAccessingSecurityScopedResource()
// Check if the temporary file already exists and remove it if necessary
if FileManager.default.fileExists(atPath: tempFileURL.path) {
try FileManager.default.removeItem(at: tempFileURL)
print("Removed existing temporary file at \(tempFileURL)")
}
// Copy the audio file to the temporary directory
try FileManager.default.copyItem(at: fileURL, to: tempFileURL)
print("Successfully copied audio file from \(fileURL) to \(tempFileURL)")
// Update your response based on the successful upload
// ...
} catch {
// Handle any errors that occur during file operations
print("Error handling audio file: \(error.localizedDescription)")
return failureResponse
}
guard let audioData = try? Data(contentsOf: tempFileURL), !audioData.isEmpty else {
print("Couldn't get audioData from intent.attachments?.first?.audioMessageFile?.data")
return failureResponse
}
Error:
FileURL: false
Audio file does not exist at file:///var/mobile/tmp/SiriMessages/BD57CB69-1E75-4429-8991-095CB90959A9.caf
is something I'm missing?
Like the image showing, the element 'Ask ChatGPT' attaches below siri search suggestion , I guess SiriKit or Spotlight can implement it.
But i searched a lot, no one introduct the detail technology.
Do anyone ever implement it?
Please help me. Thanks.