I have an app that incorporated Siri Intents and they work well. You can have Siri launch the app and it will perform the action as expected. All of my intents need to happen in the app so I do not have an extension for them.
However using the Shortcuts app, when I use my shortcut the app will just stop in my app and not continue so Siri is not receiving the handler response.
Right now my delegate will open from an action inside of the NSUserActivity call and I will use a custom class to determine which Shortcut it is and perform said action.
I do have that custom class conforming to the IntentHandler Protocol for each action and I call a .success response in the completion handler.
Heres where I think I may be mistaken. I manually call the Handler protocol inside of my custom class.
func handleSiri(_ intent: INItent) {
if intent is ActionIntent {
func handle(intent: ActionIntent, completion: #escaping (ActionIntent Response) -> Void) {
let response = DisconnectIntentResponse.init(code: .success, userActivity: nil)
print("Intent was a success")
completion(response)
}
}
}
However Siri never completes. Should I be calling this manually? Do I need to have an extension call these functions?
The only reason why this is not called is because you have not added the Voice Intent extension into your dependencies list under Build Phase section for your App Target.
Related
I'm creating shortcuts for a communication app. In this app there is a presence state (available, dnd, ...). I implemented an intent to set said presence state which worked fine. I created a "read" intent which just returns the currently set state, which works, but i can't pass the result to the next action.
For simplification and testing i created another intent just return a string, trying to process it, but i'm still unable to do that. My intent has no input parameters.
What am i missing? My Goal would be an interaction like Get Current Location offers.
class Return5IntentHandler: NSObject, Return5IntentHandling {
func handle(intent: Return5Intent, completion: #escaping (Return5IntentResponse) -> Void) {
completion(.success(result: "5"))
}
}
Classic case of searching for hours, writing a question only to find the answer 30min later yourself...
One has to explicitly set the output, which is the passed on value for following actions.
Context
I'm working on adding support for Siri Shortcuts (Intents?) to a non-trivial app. My main goal is to allow users to automatise some tasks within the app using Shortcuts.app.
I have defined MyIntent in the Intents.intentdefinition in the Intents Extension target, together with MyIntentResponse, which has an output property file: INFile.
Given the non-trivial nature of the app, I'm forwarding the intent from Intent Extension to the main app:
func handle(intent: MyIntent, completion: #escaping (MyIntentResponse) -> Void) {
// Can't handle the intent in the app extension yet, let the main app handle it
let response = MyIntentResponse(code: .continueInApp, userActivity: nil)
// TODO: How to update the response (with an output property) if the intent continues in the app?
completion(response)
}
Problem
Once the intent is forwarded to the app, the AppDelegate.application(_:continue:restorationHandler:) method is called with a user activity which also has the property interaction: INInteraction? set.
From the provided INInteraction I can get both the intent: INIntent and response: INIntentResponse to handle the intent accordingly.
What's missing, however, is a way how to communicate back to the Shortcuts.app about the result of the intent and provide the desired output property (file: INFile).
Question
Is there a way how to provide an intent response with an output property set if the intent is handled in the main app?
Details
Interestingly, there's a AppDelegate method that should handle this use case:
optional func application(_ application: UIApplication,
handle intent: INIntent,
completionHandler: #escaping (INIntentResponse) -> Void)
And the documentation says:
An app with an Intents app extension can use this method to handle an intent directly, instead of handling it in the app extension. You might use this method to implement workflows that you cannot implement easily in your extension. For example, you might use it to start or manage a user's workout session. If your app is not running, SiriKit launches your app in the background so that the Siri interface remains active.
More info: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/2887573-application
In theory, it should be possible just to call the completionHandler with a newly instantiated MyIntentResponse which would have file property set correctly.
However, this method is never called. Instead, the above mentioned AppDelegate.application(_:continue:restorationHandler:) is called.
If you use .handleInApp instead of .continueInApp in your Intents extension handle() function, then your app delegate's handleIntent() function will be called instead of the continue:restorationHandler() function. This will allow you to pass back INIntentResponse back to the extension in the completionHandler.
Whenever I receive incoming call in my VOIP application I can see the logs on my native iPhone call UI.
I wanted to make outgoing call from native iPhone callog of UI by clicking last incoming call. Like it works for WhatsApp , Skype , hangout etc.
How is it possible for outgoing call ?
Below are the methods I wrote for incoming call :
-(void)reportIncomingCall:(NSUUID*)UDID handle:(NSString*)handle;
-(CXCallController*)startCall:(NSUUID*)UDID handle:(NSString*)handle
-(void)connectCallWithCallController:(CXCallController*)controller
I know there is one more method for outgoing call.But I don't know when to call this:
- (NSUUID *)reportOutgoingCallContactIdentifier:(NSString *)identifier destination:(NSString *)name telNumber:(NSString *)telnum
When tapping on an item in the native iOS Call log, the application delegate's continueUserActivity function is called. My implementation looks something like this in Swift:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
self.startCallHandle = userActivity.startCallHandle
// Cache call handle here, and make call using cached call handle
// when applicationDidBecomeActive is called
return true
}
startCallHandle is defined in file NSUserActivity+StartCallConvertible.swift, as seen in the SpeakerBox sample project.
In your app's Info.plist, you must have INStartAudioCallIntent and/or INStartVideoCallIntent. Once again, see the SpeakerBox example app for details.
After I first build and run my intents project, requesting something from Siri always returns with Sorry, you will need to continue in the app
However, it works every time after that until I rebuild.
I put breakpoints in handler:
override func handler(for intent: INIntent) -> Any {
return self
}
and in handle:
func handle(requestRide intent: INRequestRideIntent, completion: #escaping (INRequestRideIntentResponse) -> Void) {
...
}
The breakpoint in handler is hit a few seconds after Siri says continue in app.
SiriKit is very picky on waiting. Your first run will time out because of the time it takes Xcode to attach the debugger to Siri's process.
I've just learned to live with it.
I am creating an Apple Watch app which has a button that makes a call to the parent iOS app.
WKInterfaceController.openParentApplication(message, reply: {
(response: [NSObject:AnyObject]!, error: NSError!) in
// processing data from iOS app
})
The iOS app responds by retrieving some reminders using EventKit, like so:
eventStore.fetchRemindersMatchingPredicate(predicate, completion: {
[unowned self] reminders in
// sending back data as reply to Apply Watch
})
When the parent app is in the foreground, everything works like a charm. But when I make the app go to the background, something strange happens. The call to EKEventStore, eventStore.fetchRemindersMatchingPredicate(predicate), never calls my completion block. So I never send a reply back to the Apple Watch because of that?
So what is going wrong? I suspect a bug in EventKit, or maybe calling EventKit in the background is not supported. Can anyone shed some light on this?
I figured it out! Before letting EKEventStore spawn an async process, I have to start a background task. When I complete the async call, I have to explicitly end the background task.
// First let iOS know you're starting a background task
let taskIdentifier = application.beginBackgroundTaskWithExpirationHandler() {
() -> Void in
// Do something when task is taking too long
}
// Then do the async call to EKEventStore
eventStore.fetchRemindersMatchingPredicate(predicate, completion: {
[unowned self] reminders in
// Do what I have to do, and afterwards end the background task:
application.endBackgroundTask(taskIdentifier)
})