2

我的自定义意图的 Objective-C 实现中的 IntentHandler 无法接收来自语音激活快捷方式的呼叫。当使用 Siri 调用捐赠的交互时,我观察到我在控制台应用程序中收到以下错误,声称意图的意图处理程序方法未实现:

-[INIntentDeliverer _invokeIntentHandlerMethodForIntent:intentHandler:parameterNamed:keyForSelectors:executionHandler:unimplementedHandler:] _invokeIntentHandlerMethodForIntent sirikit.intent.voice_commands.RunVoiceCommandIntent


-[WFRVCIntentHandler stateMachineForIntent:] Created state machine <WFRVCStateMachine: 0x102e23970 state=WaitingForServer phase=Unknown> for intent with identifier 8A87FC68-329D-49FF-B534-B0A5821854CA


-[INIntentDeliverer _invokeIntentHandlerMethodForIntent:intentHandler:parameterNamed:keyForSelectors:executionHandler:unimplementedHandler:] _invokeIntentHandlerMethodForIntent sirikit.intent.voice_commands.RunVoiceCommandIntent

此错误与尝试使用语音命令触发自定义意图导致 iOS 调用我的 appDelegate,特别是 application:continueUserActivity:restorationHandler: 的事实一致。根据文档,restoreHandler 仅应在未处理意图且必须由主应用程序处理的情况下调用。

由于 Objective-C 实现的文档很少,我无法弄清楚我缺少什么。我试图将 Siri Shortcuts 的示例 SoupChef 应用程序实现映射到我的实现。我无法弄清楚我哪里出错了。这是我的实现(对不起所有细节,但我希望你能看到一些错误):

首先,我实现了两个额外的目标;一个共享框架和一个意图扩展。我还实现了一个意图定义文件。

这是我的目标的图像:

在此处输入图像描述

W_P_r 是主应用程序,W_P_rKit 是共享框架,PartsListManagerIntents 是 Intents 扩展。

接下来,这是我的 Intents 定义文件和它所属的目标成员:

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

我还在 main 目标和 PartsListIntentManager 目标的 add 的功能部分中添加了一个应用程序组。我将 Siri 功能添加到主要目标。

所有这些都会自动创建一些代码,包括 PartsListManagerIntents 目标中的默认 IntentHandler.m 和 info.plist。我已将 info.plist 更新如下:

在此处输入图像描述

这是自动生成的 IntentHandler(我已对其进行修改以记录活动并调用驻留在 W_P_rKit 共享框架中的特定意图处理程序:

#import "IntentHandler.h"
#import <Intents/Intents.h>
#import <W_P_rKit/W_P_rKit.h>
#import "CreatePartsListIntentHandler.h"
#import "P__tHandler.h"
#import <os/log.h>

@interface IntentHandler () /* <CreatePartsListIntentHandling, P__tHandling> */

@end


@implementation IntentHandler

- (id)handlerForIntent:(INIntent *)intent {

    os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEBUG, "handlerForIntent: Reached IntentHandler.");

    if ([intent.identifier isEqualToString:@"P__rIntent"]) {
        NSLog(@"P__rIntent");
        return [[P__rIntentHandler alloc] init];
    }
    else if ([intent.identifier isEqualToString:@"CreatePartsListIntent"]) {
        NSLog(@"CreatePartsListIntent");
        os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEBUG, "handlerForIntent: IntentHandler Received CreatePartsListIntent.");
        return [[CreatePartsListIntentHandler alloc] init];
    
    }
    return self;
}

请注意,CreatePartsListIntentHandler 是一个实现 CreatePartsListIntentHandling 协议的类(IntentHandler 的解析、确认和处理方法)。

现在这里是应该触发 iOS 调用 IntentHandler 的相关实现:

在我的应用程序中,当用户填写新项目的名称时,我拨打电话以捐赠互动,如下所示:

CreatePartsListIntent *data = [[CreatePartsListIntent alloc] init];
data.projectName = [projectPlistName copy];
data.quantity = [NSNumber numberWithInteger : currentProjectQuantity];
[[W_P_rDonationManager sharedInstance] donateCreatePartsListIntent : data];

对 donateCreatePartsListIntent 的调用执行以下操作:

data.suggestedInvocationPhrase = @"Create Parts List";
INInteraction* interaction = [[INInteraction alloc] initWithIntent:data response:nil];
[interaction donateInteractionWithCompletion:^(NSError * _Nullable error) { ... }

一旦用户创建了空的部件列表(强制进行上述交互捐赠),视图控制器将显示一个“添加 Siri 快捷方式”按钮。点击按钮会自动调用以下方法来创建快捷方式:

-(void) addCreatePartsListShortcutWasTapped {
    CreatePartsListIntent *intentWithData = [[WoodPickerDonationManager sharedInstance] prepareCreatePartsListIntent :
                                         @"" withNumberOfAssemblies : 1];
    INShortcut *shortcut = [[INShortcut alloc] initWithIntent:intentWithData];
    INUIAddVoiceShortcutViewController *addSiri = [[INUIAddVoiceShortcutViewController alloc] initWithShortcut:shortcut];
    addSiri.delegate = self;    
    [self presentViewController:addSiri animated:YES completion: nil];
}

对 prepareCreatePartsListIntent 的调用执行以下操作:

-(CreatePartsListIntent *) prepareCreatePartsListIntent : (NSString *) partsListName withNumberOfAssemblies : (NSInteger) quantity {

    CreatePartsListIntent *intentWithData = [[CreatePartsListIntent alloc] init];

    intentWithData.projectName = partsListName;
    intentWithData.quantity = [NSNumber numberWithInteger: quantity];
    intentWithData.suggestedInvocationPhrase = @"Create Parts List";

    return intentWithData;
}

这确实会创建一个在快捷方式应用程序中可见的快捷方式。单击快捷方式或说出调用短语将直接将您带到应用委托的 application:userActivity:restorationHandler。但它不调用 IntentHandler。

为什么我的 IntentHandler 没有被调用?为什么 iOS 会发送错误消息 _invokeIntentHandlerMethodForIntent:intentHandler:parameterNamed:keyForSelectors:executionHandler:unimplementedHandler:?

实际上,我已经为此苦苦挣扎了好几个星期。任何帮助或提示都会很有帮助。

4

3 回答 3

0

我怀疑您的 .intentdefinition 文件的 Target Membership 选择过于严格。我相信您应该为所有三个目标将其设置为“公共意图类”(屏幕截图显示其中两个目标“没有生成的类”。

我不确定这是问题的原因,但值得尝试查看问题或控制台日志消息是否消失。

我会注意到,我在混合 Objective C / Swift 项目时遇到了与您相同的症状。AppDelegate 在 Objective C 中,而大多数其他代码在 Swift 中。我还看到 AppDelegate 方法被调用,但意图处理程序没有。尽管在我的情况下,我将所有目标都设置为“公共意图类”,并且控制台没有显示您描述的相同日志行。我怀疑如果您进行上述更改,它可能会使这些日志行消失而不解决核心问题——如果是这样,这将表明这些日志行是一个红鲱鱼。

关于我的案例的其他一些注意事项可能与您的案例相似或不同:(1)我正在使用带有“旧版构建系统”的旧 Xcode 项目,我最近尝试在其中添加 Siri 支持。(2) 我正在使用 Xcode 13.1 构建,但在较旧的 iOS 12.4.6 设备上进行测试,因为这是我们用户的最低操作系统版本。我还没有尝试过更新的 iOS 版本。

编辑:请参阅我的其他答案以解决我的问题。我在旧版 iOS 12.4.6 设备上使用 Xcode 13.1 上的旧版构建系统进行了这项工作。

于 2021-12-21T03:12:07.047 回答
0

检查所有目标(Siri Intent 目标、IntentUI 目标等)的 XCode Build Settings 是否都在 Deployment Info 下指定了相同的最低 iOS 版本,如下所示:

在此处输入图像描述

如果 Intent 目标指定的最低 iOS 版本高于您用于测试的设备,则 IntentHandler 将永远不会被调用,而是会调用主目标中的 AppDelegate 方法。就我而言,一旦我将部署信息更改为较低的 iOS 版本,IntentHandler 立即开始被调用,并且不再调用 AppDelegate 方法。

这个想法归功于 Ondřej Korol:https ://stackoverflow.com/a/59500997/1461050

在为旧 iOS 版本修改旧项目时尤其可能发生此问题,因为当您在 XCode 中创建新目标时,它会将 DeploymentInfo 默认为 XCode 的默认值,即使它与主目标的 DeploymentInfo 不匹配。不幸的是,这是导致 SiriKit 开发非常脆弱的众多问题之一。一件小事错了,整个事情就默默地失败了!

于 2021-12-29T14:44:55.263 回答
0

经过几个月的挫折,我终于得到了这个工作。我必须创建一个实现了自定义意图的新应用程序。我剪切并粘贴了上面列出的所有相关代码,并且瞧,它确实有效。然后我煞费苦心地检查了每一个设置,直到我发现了问题。

Build Phases/Embed App Extensions 下有一个复选框(在项目导航器中选择了我的项目,然后选择了我的 App 目标)。此复选框“仅在安装时复制”已选中。取消选中此项后,一切正常。因为我不知道这是什么,所以在将其视为某人关于意图的帖子的解决方案后,我一定是绝望地检查了它。

此外,事实证明,我在上面的问题陈述中提到的错误并不是任何后果的错误。它们无论如何都会发生。

于 2022-01-19T17:52:35.133 回答