如何管理 Siri 意图启动我的应用程序并将数据传输到应用程序?
How to manage that Siri intent launches my app and transfers data to the app?
经过几个小时的搜索,我请求你的帮助。
我正在编写一个应用程序来管理我的库存。作为通过专用视图“AddNewEntry”手动输入库存项目的替代方法,我想使用 Siri 和 intent 来捕获“Store item 意义上的数据将 放在 部分 中”。 item, place 和 section 是我想通过 Siri 获取的属性,然后将它们存储在我的数据库使用视图“AddNewEntry”,我在其中处理这些数据的手动输入、检查和存储。对于来自意图的新数据的移交,我想 post 向我的 mainViewController 发出通知,然后启动 segue 到要显示的视图和 check/store 新条目。
我已经通过意图定义设置了意图(“VorratAdd”),快捷方式似乎工作正常并收集数据。但是快捷方式不会 return 到我的应用程序,并以弹出视图结束,消息如下(我从德语翻译):
"Vorrat Add" couldn't be executed. Tasks takes too long to be closed.
Retry again"
我的设置如下:
我的意图处理程序 VorratAdd:
import Foundation
import Intents
import UIKit
class VorratAddIntentHandler: NSObject, VorratAddIntentHandling {
func handle(intent: VorratAddIntent, completion: @escaping (VorratAddIntentResponse) -> Void) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "NewVorratInputFromSiri"), object: intent)
}
func resolveBezeichnung(for intent: VorratAddIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
if intent.item == " " {
completion(INStringResolutionResult.needsValue())
} else {
completion(INStringResolutionResult.success(with: intent.bezeichnung ?? "_"))
}
}
func resolveOrt(for intent: VorratAddIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
if intent.place == " " {
completion(INStringResolutionResult.needsValue())
} else {
completion(INStringResolutionResult.success(with: intent.ort ?? "_"))
}
}
func resolveFach(for intent: VorratAddIntent, with completion: @escaping (VorratAddFachResolutionResult) -> Void) {
if intent.section == 0 {
completion(VorratAddFachResolutionResult.needsValue())
} else {
completion(VorratAddFachResolutionResult.success(with: intent.fach as! Int))
}
}
}
IntentHandler.swift:
import UIKit
import Intents
class IntentHandler: INExtension {
override func handler(for intent: INIntent) -> Any {
guard intent is VorratAddIntent else {
fatalError("unhandled Intent error: \(intent)")
}
return VorratAddIntentHandler()
}
}
捐赠 在我的 mainViewController“HauptVC”中:
extension HauptVC {
func initSiriIntent() {
let intent = VorratAddIntent()
intent.suggestedInvocationPhrase = "Neuer Vorrat"
intent.item = " "
intent.location = " "
intent.section = 0
let interaction = INInteraction(intent: intent, response: nil)
interaction.donate { (error) in
if error != nil {
if let error = error as NSError? {
print("Interaction donation fehlgeschlagen: \(error.description)")
} else {
print("Interaction donation erfolgreich")
}
}
}
}
}
AppDelegate:
import UIKit
import Intents
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var rootViewController: UINavigationController?
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == "VorratAddIntent" {
restorationHandler(rootViewController!.viewControllers)
return true
}
return false
}
场景代理:
import UIKit
import Intents
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let app = UIApplication.shared.delegate as! AppDelegate
var rootVC: UINavigationController?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
rootVC = self.window?.rootViewController as? UINavigationController
app.rootViewController = rootVC
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == "VorratAddIntent" {
app.rootViewController = rootVC
restorationHandler(rootVC!.viewControllers)
return true
}
return false
}
有人知道我做错了什么吗?非常感谢您的支持。
我认为您缺少一种方法实现“确认”
func confirm(intent: VorratAddIntent, completion: @escaping (VorratAddIntentResponse) -> Void) {
completion(.init(code: .success, userActivity: nil))
}
编辑:
此处documentation用于确认意向详情
编辑 2:
您必须在“句柄”方法中调用完成处理程序
func handle(intent: VorratAddIntent, completion: @escaping (VorratAddIntentResponse) -> Void) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "NewVorratInputFromSiri"), object: intent)
completion(your response)
}
编辑 3 - 将数据从扩展程序 (Siri) 传递到应用:
NotificationCenter 在扩展程序和主应用程序之间不起作用,因为它们没有以任何方式连接,这是完全不同的过程。
有不同的方法可以实现:
App Group
:
Here 是核心数据和扩展的示例,您可以如何共享数据库
UserDefaults with AppGroup
:
您也可以使用 UserDefaults(suiteName: your app group name)
NSUserActivity
:
SiriKit 最方便的方法 apple doc
NSUserActivity 示例
您将在 handle 方法中创建 NSUserActivity 实例,如下所示:
func handle(intent: VorratAddIntent, completion: @escaping (VorratAddIntentResponse) -> Void) {
let response = VorratAddIntentResponse.success()
let activity = NSUserActivity(activityType:NSStringFromClass(VorratAddIntent.self))
activity.addUserInfoEntries(from: [
"task_id" : your task id
])
response.userActivity = activity
completion(response)
}
而且你可以在你的应用程序中处理用户信息,如果应用程序已经 运行 你提取 task_id 值如下:
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard userActivity.interaction?.intent is VorratAddIntent else { return }
#if DEBUG
print("Siri Intent user info: \(String(describing: userActivity.userInfo))")
#endif
guard let taskID = userActivity.userInfo?["task_id"] as? String
else { return }
}
但是如果你的应用不在后台运行,你需要在场景中处理用户活动(willConnectTo:)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
for activity in connectionOptions.userActivities {
self.scene(scene, continue: activity)
}
}
经过几个小时的搜索,我请求你的帮助。
我正在编写一个应用程序来管理我的库存。作为通过专用视图“AddNewEntry”手动输入库存项目的替代方法,我想使用 Siri 和 intent 来捕获“Store item 意义上的数据将 放在 部分 中”。 item, place 和 section 是我想通过 Siri 获取的属性,然后将它们存储在我的数据库使用视图“AddNewEntry”,我在其中处理这些数据的手动输入、检查和存储。对于来自意图的新数据的移交,我想 post 向我的 mainViewController 发出通知,然后启动 segue 到要显示的视图和 check/store 新条目。
我已经通过意图定义设置了意图(“VorratAdd”),快捷方式似乎工作正常并收集数据。但是快捷方式不会 return 到我的应用程序,并以弹出视图结束,消息如下(我从德语翻译):
"Vorrat Add" couldn't be executed. Tasks takes too long to be closed. Retry again"
我的设置如下:
我的意图处理程序 VorratAdd:
import Foundation
import Intents
import UIKit
class VorratAddIntentHandler: NSObject, VorratAddIntentHandling {
func handle(intent: VorratAddIntent, completion: @escaping (VorratAddIntentResponse) -> Void) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "NewVorratInputFromSiri"), object: intent)
}
func resolveBezeichnung(for intent: VorratAddIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
if intent.item == " " {
completion(INStringResolutionResult.needsValue())
} else {
completion(INStringResolutionResult.success(with: intent.bezeichnung ?? "_"))
}
}
func resolveOrt(for intent: VorratAddIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
if intent.place == " " {
completion(INStringResolutionResult.needsValue())
} else {
completion(INStringResolutionResult.success(with: intent.ort ?? "_"))
}
}
func resolveFach(for intent: VorratAddIntent, with completion: @escaping (VorratAddFachResolutionResult) -> Void) {
if intent.section == 0 {
completion(VorratAddFachResolutionResult.needsValue())
} else {
completion(VorratAddFachResolutionResult.success(with: intent.fach as! Int))
}
}
}
IntentHandler.swift:
import UIKit
import Intents
class IntentHandler: INExtension {
override func handler(for intent: INIntent) -> Any {
guard intent is VorratAddIntent else {
fatalError("unhandled Intent error: \(intent)")
}
return VorratAddIntentHandler()
}
}
捐赠 在我的 mainViewController“HauptVC”中:
extension HauptVC {
func initSiriIntent() {
let intent = VorratAddIntent()
intent.suggestedInvocationPhrase = "Neuer Vorrat"
intent.item = " "
intent.location = " "
intent.section = 0
let interaction = INInteraction(intent: intent, response: nil)
interaction.donate { (error) in
if error != nil {
if let error = error as NSError? {
print("Interaction donation fehlgeschlagen: \(error.description)")
} else {
print("Interaction donation erfolgreich")
}
}
}
}
}
AppDelegate:
import UIKit
import Intents
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var rootViewController: UINavigationController?
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == "VorratAddIntent" {
restorationHandler(rootViewController!.viewControllers)
return true
}
return false
}
场景代理:
import UIKit
import Intents
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let app = UIApplication.shared.delegate as! AppDelegate
var rootVC: UINavigationController?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
rootVC = self.window?.rootViewController as? UINavigationController
app.rootViewController = rootVC
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == "VorratAddIntent" {
app.rootViewController = rootVC
restorationHandler(rootVC!.viewControllers)
return true
}
return false
}
有人知道我做错了什么吗?非常感谢您的支持。
我认为您缺少一种方法实现“确认”
func confirm(intent: VorratAddIntent, completion: @escaping (VorratAddIntentResponse) -> Void) {
completion(.init(code: .success, userActivity: nil))
}
编辑:
此处documentation用于确认意向详情
编辑 2:
您必须在“句柄”方法中调用完成处理程序
func handle(intent: VorratAddIntent, completion: @escaping (VorratAddIntentResponse) -> Void) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "NewVorratInputFromSiri"), object: intent)
completion(your response)
}
编辑 3 - 将数据从扩展程序 (Siri) 传递到应用:
NotificationCenter 在扩展程序和主应用程序之间不起作用,因为它们没有以任何方式连接,这是完全不同的过程。
有不同的方法可以实现:
App Group
:
Here 是核心数据和扩展的示例,您可以如何共享数据库UserDefaults with AppGroup
:
您也可以使用UserDefaults(suiteName: your app group name)
NSUserActivity
:
SiriKit 最方便的方法 apple doc
NSUserActivity 示例
您将在 handle 方法中创建 NSUserActivity 实例,如下所示:
func handle(intent: VorratAddIntent, completion: @escaping (VorratAddIntentResponse) -> Void) {
let response = VorratAddIntentResponse.success()
let activity = NSUserActivity(activityType:NSStringFromClass(VorratAddIntent.self))
activity.addUserInfoEntries(from: [
"task_id" : your task id
])
response.userActivity = activity
completion(response)
}
而且你可以在你的应用程序中处理用户信息,如果应用程序已经 运行 你提取 task_id 值如下:
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard userActivity.interaction?.intent is VorratAddIntent else { return }
#if DEBUG
print("Siri Intent user info: \(String(describing: userActivity.userInfo))")
#endif
guard let taskID = userActivity.userInfo?["task_id"] as? String
else { return }
}
但是如果你的应用不在后台运行,你需要在场景中处理用户活动(willConnectTo:)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
for activity in connectionOptions.userActivities {
self.scene(scene, continue: activity)
}
}