Handle 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

CarPlay / Siri: single message display
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?
3
0
634
Sep ’24
Can't get audio data from INSendMessageIntent
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?
2
1
757
Oct ’24