Skip to content
Permalink
Browse files

Updates to shortcut/intent APIs (#254)

* Fix addVoiceShortcuts not working for intents, auto-set suggestedVoicePhrase property
* Add `shortcut()` function
* New `editShortcut` functions on bindings
* Add new "add/edit shortcut" result enum types
  • Loading branch information...
marcpalmer committed Apr 19, 2019
1 parent b7f4e66 commit a96ccebc6b42c66653cb3455711a2065c58c20ec
@@ -121,7 +121,8 @@ public protocol Action {
/// A suggested Siri Shortcut phrase to show in the Siri UI when adding a shortcut or registering an `NSUserActivity` for
/// this action.
///
/// - note: This value is only used if your `activityEligibility` include `.prediction`.
/// - note: This value is only used if your `activityEligibility` include `.prediction`, or you when your action creates
/// an `INIntent` to donate.
static var suggestedInvocationPhrase: String? { get }

#if canImport(Intents)
@@ -18,10 +18,10 @@ final public class PerformIncomingActivityAction: UIAction {
case noActivityTypeMappingFound
}

/// Attempt to resolve the URL against a URL mapping, and execute the action.
/// Attempt to perform the action associated with this activity.
///
/// The completion outcome will fail with error `noURLMappingFound` if the URL does not map to anything
/// that Flint knows about.
/// The completion outcome will fail with an error if the activity does not map to anything
/// that Flint knows about using the `activityType`
public static func perform(context: ActionContext<NSUserActivity>, presenter: PresentationRouter, completion: Action.Completion) -> Action.Completion.Status {
context.logs.development?.debug("Finding action executor for activity: \(context.input.activityType), userInfo \(String(describing: context.input.userInfo))")

@@ -8,23 +8,28 @@
import Foundation
import UIKit
#if os(iOS)
#if canImport(Intents)
import Intents
#endif
#endif

#if canImport(Network) && os(iOS)
extension StaticActionBinding {

/// Call to invoke the system "Add Voice Shortcut" view controller for the given input to the conditionally-available
/// action represented by this action request. The action must support creating an `INIntent` for a custom intent extension
/// to be invoked, or creating an `NSUserActivity` using Flint's Activities conventions.
/// action represented by this action request. The action must support creating an `NSUserActivity` using Flint's Activities conventions.
///
/// This will create a shortcut that invokes the `INIntent` returned by the `Action`'s `intent(for:)` function.
/// If that function returns nil (or is not defined by your `Action`), it will attempt to create an `NSUserActivity`
/// for the `Action` and instead use that. If the `Action` does not support `Activities`, this will fail.
/// This will create a shortcut that invokes the activity created for the `Action`'s.
/// If the `Action` does not support `Activities`, this will fail.
///
/// - param input: The input to pass to the action when it is later invoked from the Siri Shortcut by the user.
/// - param presenter: The `UIViewController` to use to present the view controller
@available(iOS 12, *)
public func addVoiceShortcut(for input: ActionType.InputType, presenter: UIViewController) {
VoiceShortcuts.addVoiceShortcut(action: ActionType.self, feature: FeatureType.self, for: input, presenter: presenter)
public func addVoiceShortcut(for input: ActionType.InputType,
presenter: UIViewController,
completion: @escaping (_ result: AddVoiceShortcutResult) -> Void) {
VoiceShortcuts.addVoiceShortcut(action: ActionType.self, feature: FeatureType.self, for: input, presenter: presenter, completion: completion)
}
}

@@ -65,12 +70,59 @@ extension StaticActionBinding where ActionType: IntentAction {
return .init(outcome: outcome)
}

/// Call to invoke the system "Add Voice Shortcut" view controller for the given input to the conditionally-available
/// action represented by this action request. The action must support creating an `INIntent` for a custom intent extension
/// to be invoked.
///
/// This will create a shortcut that invokes the `INIntent` returned by the `Action`'s `intent(for:)` function.
/// If that function returns nil (or is not defined by your `Action`), it will attempt to create an `NSUserActivity`
/// for the `Action` and instead use that. If the `Action` does not support `Activities`, this will fail.
///
/// - param input: The input to pass to the action when it is later invoked from the Siri Shortcut by the user.
/// - param presenter: The `UIViewController` to use to present the view controller
///
/// - note: This variant exists for the specialisation that will call `intent(for:)` on the Action to create an
/// an intent for the shortcut.
@available(iOS 12, *)
public func addVoiceShortcut(input: ActionType.InputType,
presenter: UIViewController,
completion: @escaping (_ result: AddVoiceShortcutResult) -> Void) {
VoiceShortcuts.addVoiceShortcut(action: ActionType.self,
feature: FeatureType.self,
for: input,
presenter: presenter,
completion: completion)
}

@available(iOS 12, *)
public func editVoiceShortcut(_ shortcut: INVoiceShortcut,
presenter: UIViewController,
completion: @escaping (_ result: EditVoiceShortcutResult) -> Void) {
VoiceShortcuts.editVoiceShortcut(shortcut,
presenter: presenter,
completion: completion)
}

/// Create an `INShortcut` instance for the given input. Use when pre-registering shortcuts with `INVoiceShortcuteCenter`
@available(iOS 12, *)
public func shortcut(input: ActionType.InputType) -> INShortcut? {
guard let shortcutIntent = ActionType.intent(for: input) else {
return nil
}
return INShortcut(intent: shortcutIntent)
}

/// Donate an intent-based shortcut that will invoke this `Action` to Siri for the given input.
@available(iOS 12, *)
public func donateToSiri(for input: ActionType.InputType) {
guard let intent = ActionType.intent(for: input) else {
flintUsageError("Cannot donate intent to Siri, action type \(ActionType.self) did not return an intent for input: \(input).")
}

if intent.suggestedInvocationPhrase == nil {
intent.suggestedInvocationPhrase = ActionType.suggestedInvocationPhrase
}

if let request = IntentShortcutDonationFeature.donateShortcut.request() {
let intentWrapper = FlintIntentWrapper(intent: intent)
request.perform(input: intentWrapper)
@@ -8,24 +8,27 @@
import Foundation
import UIKit
#if os(iOS)
#if canImport(Intents)
import Intents
#endif
#endif

// Workaround for inability to compile against just iOS 12+, using the new "Network" framework as an indicator
#if canImport(Network) && os(iOS)
extension VerifiedActionBinding {

/// Call to invoke the system "Add Voice Shortcut" view controller for the given input to the conditionally-available
/// action represented by this action request. The action must support creating an `INIntent` for a custom intent extension
/// to be invoked, or creating an `NSUserActivity` using Flint's Activities conventions.
/// action represented by this action request. The action must support creating an `NSUserActivity` using Flint's Activities conventions.
///
/// This will create a shortcut that invokes the `INIntent` returned by the `Action`'s `intent(for:)` function.
/// If that function returns nil (or is not defined by your `Action`), it will attempt to create an `NSUserActivity`
/// for the `Action` and instead use that. If the `Action` does not support `Activities`, this will fail.
/// This will create a shortcut that invokes the activity created for the `Action`'s.
/// If the `Action` does not support `Activities`, this will fail.
///
/// - param input: The input to pass to the action when it is later invoked from the Siri Shortcut by the user.
/// - param presenter: The `UIViewController` to use to present the view controller
@available(iOS 12, *)
public func addVoiceShortcut(for input: ActionType.InputType, presenter: UIViewController) {
VoiceShortcuts.addVoiceShortcut(action: ActionType.self, feature: FeatureType.self, for: input, presenter: presenter)
public func addVoiceShortcut(for input: ActionType.InputType, presenter: UIViewController, completion: @escaping (_ result: AddVoiceShortcutResult) -> Void) {
VoiceShortcuts.addVoiceShortcut(action: ActionType.self, feature: FeatureType.self, for: input, presenter: presenter, completion: completion)
}
}

@@ -65,12 +68,59 @@ extension VerifiedActionBinding where ActionType: IntentAction {
return .init(outcome: outcome)
}

/// Call to invoke the system "Add Voice Shortcut" view controller for the given input to the conditionally-available
/// action represented by this action request. The action must support creating an `INIntent` for a custom intent extension
/// to be invoked.
///
/// This will create a shortcut that invokes the `INIntent` returned by the `Action`'s `intent(for:)` function.
/// If that function returns nil (or is not defined by your `Action`), it will attempt to create an `NSUserActivity`
/// for the `Action` and instead use that. If the `Action` does not support `Activities`, this will fail.
///
/// - param input: The input to pass to the action when it is later invoked from the Siri Shortcut by the user.
/// - param presenter: The `UIViewController` to use to present the view controller
///
/// - note: This variant exists for the specialisation that will call `intent(for:)` on the Action to create an
/// an intent for the shortcut.
@available(iOS 12, *)
public func addVoiceShortcut(input: ActionType.InputType,
presenter: UIViewController,
completion: @escaping (_ result: AddVoiceShortcutResult) -> Void) {
VoiceShortcuts.addVoiceShortcut(action: ActionType.self,
feature: FeatureType.self,
for: input,
presenter: presenter,
completion: completion)
}

@available(iOS 12, *)
public func editVoiceShortcut(_ shortcut: INVoiceShortcut,
presenter: UIViewController,
completion: @escaping (_ result: EditVoiceShortcutResult) -> Void) {
VoiceShortcuts.editVoiceShortcut(shortcut,
presenter: presenter,
completion: completion)
}

/// Create an `INShortcut` instance for the given input. Use when pre-registering shortcuts with `INVoiceShortcuteCenter`
@available(iOS 12, *)
public func shortcut(input: ActionType.InputType) -> INShortcut? {
guard let shortcutIntent = ActionType.intent(for: input) else {
return nil
}
return INShortcut(intent: shortcutIntent)
}

/// Donate an intent-based shortcut to this `Action` to Siri for the given input.
@available(iOS 12, *)
public func donateToSiri(for input: ActionType.InputType) {
public func donateToSiri(input: ActionType.InputType) {
guard let intent = ActionType.intent(for: input) else {
flintUsageError("Cannot donate intent to Siri, action type \(ActionType.self) did not return an intent for input: \(input).")
}

if intent.suggestedInvocationPhrase == nil {
intent.suggestedInvocationPhrase = ActionType.suggestedInvocationPhrase
}

if let request = IntentShortcutDonationFeature.donateShortcut.request() {
let intentWrapper = FlintIntentWrapper(intent: intent)
request.perform(input: intentWrapper)
Oops, something went wrong.

0 comments on commit a96cceb

Please sign in to comment.
You can’t perform that action at this time.