首页 > 解决方案 > 如何管理 Siri 意图启动我的应用程序并将数据传输到应用程序?

问题描述

经过数小时的搜索,我请求您的帮助。

我正在编写一个应用程序来管理我的库存。作为通过专用视图“AddNewEntry”手动输入库存项目的替代方法,我想使用 Siri 并打算在“将项目存储在部分中的位置”的意义上捕获数据。itemplacesection是我想通过 Siri 获取的属性,然后使用视图“AddNewEntry”将它们存储在我的数据库中,我在其中处理这些数据的手动输入、检查和存储。为了从意图中移交新数据,我想向我的 mainViewController 发布通知,然后启动对 View 的 segue 以显示和检查/存储新条目。

我已经通过意图定义设置了意图(“VorratAdd”),快捷方式似乎工作正常并收集数据。但是快捷方式不会返回到我的应用程序并以弹出视图结束,并显示如下消息(我的德语翻译):

无法执行“Vorrat Add”。任务需要很长时间才能关闭。重试” 在此处输入图像描述

我的设置如下:

我的意图处理程序 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")
                }
            }
            
        }
    }
}

应用委托:

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
    }

有谁知道我做错了什么?非常感谢你的支持。

标签: iosswiftsirisirishortcuts

解决方案


我认为您缺少一种方法实现“确认”

func confirm(intent: VorratAddIntent, completion: @escaping (VorratAddIntentResponse) -> Void) {
    completion(.init(code: .success, userActivity: nil))
}

编辑:

这是用于确认意图详细信息的文档

编辑2:

您必须在“handle”方法中调用完成处理程序

 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
    是核心数据和扩展的示例,您如何共享数据库

  • UserDefaults with AppGroup:
    你也可以用UserDefaults(suiteName: your app group name)

  • NSUserActivity: SiriKit苹果文档
    最方便的方式

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)
        }
    }

推荐阅读