iOS SiriKit 中 handle 与 confirm delegates 未调用
结论
只有在意图解析完成,并且 confirmSendPayment:completion: 返回 INSendPaymentIntentResponseCodeReady 后,系统才会调用 handleSendPayment:completion:。
如果 Siri 提示“需要在 App 中继续”,通常是因为确认阶段返回了 FailureRequiringAppLaunch,或者 Intent Extension 出现超时、崩溃、配置错误、方法签名不正确等问题。排查时,应重点查看确认回调实际传给 Siri 的 responseCode,不能只确认方法是否被调用。
正常调用顺序
支付 Intent 通常按以下顺序执行:
resolvePayeeForSendPayment:withCompletion:resolveCurrencyAmountForSendPayment:withCompletion:confirmSendPayment:completion:handleSendPayment:completion:
所有参数解析成功,并且确认阶段返回 Ready 后,Siri 才会调用 handleSendPayment。
正确的 Objective-C 实现
#import <Intents/Intents.h>
@interface IntentHandler () <INSendPaymentIntentHandling>
@end
@implementation IntentHandler
- (id)handlerForIntent:(INIntent *)intent
{
if ([intent isKindOfClass:[INSendPaymentIntent class]]) {
return self;
}
return self;
}
- (void)resolvePayeeForSendPayment:(INSendPaymentIntent *)intent
withCompletion:
(void (^)(INPersonResolutionResult *resolutionResult))completion
{
INPerson *payee = intent.payee;
if (payee == nil) {
completion([INPersonResolutionResult needsValue]);
return;
}
// 实际项目中应根据联系人、账户或服务端数据确认收款人。
completion([INPersonResolutionResult successWithResolvedPerson:payee]);
}
- (void)resolveCurrencyAmountForSendPayment:(INSendPaymentIntent *)intent
withCompletion:
(void (^)(INCurrencyAmountResolutionResult *resolutionResult))completion
{
INCurrencyAmount *amount = intent.currencyAmount;
if (amount == nil || amount.amount == nil) {
completion([INCurrencyAmountResolutionResult needsValue]);
return;
}
completion(
[INCurrencyAmountResolutionResult
successWithResolvedCurrencyAmount:amount]
);
}
- (void)confirmSendPayment:(INSendPaymentIntent *)intent
completion:
(void (^)(INSendPaymentIntentResponse *response))completion
{
// 此处只检查支付是否具备执行条件,不要返回 Success。
BOOL paymentCanProceed = YES;
INSendPaymentIntentResponseCode code =
paymentCanProceed
? INSendPaymentIntentResponseCodeReady
: INSendPaymentIntentResponseCodeFailure;
INSendPaymentIntentResponse *response =
[[INSendPaymentIntentResponse alloc] initWithCode:code
userActivity:nil];
completion(response);
}
- (void)handleSendPayment:(INSendPaymentIntent *)intent
completion:
(void (^)(INSendPaymentIntentResponse *response))completion
{
// 在这里执行真正的支付操作。
BOOL paymentSucceeded = YES;
INSendPaymentIntentResponseCode code =
paymentSucceeded
? INSendPaymentIntentResponseCodeSuccess
: INSendPaymentIntentResponseCodeFailure;
INSendPaymentIntentResponse *response =
[[INSendPaymentIntentResponse alloc] initWithCode:code
userActivity:nil];
completion(response);
}
@end
其中最重要的是:
INSendPaymentIntentResponseCodeReady
如果确认阶段返回其他状态,尤其是:
INSendPaymentIntentResponseCodeFailureRequiringAppLaunch
系统会要求用户打开主 App,不再调用 handleSendPayment:completion:。
检查 Intent Extension 配置
确认 Intent Extension 的 Info.plist 包含支付 Intent:
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>IntentsSupported</key>
<array>
<string>INSendPaymentIntent</string>
</array>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.intents-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).IntentHandler</string>
</dict>
在 Objective-C 项目中,NSExtensionPrincipalClass 也可以直接填写类名,具体写法取决于 Extension Target 的模块配置。无论采用哪种写法,系统都必须能实例化实际实现了 INSendPaymentIntentHandling 的类。
还需要逐项检查:
handlerForIntent:返回的对象是否实现了INSendPaymentIntentHandling。INSendPaymentIntent是否已经加入IntentsSupported。- App 和 Intent Extension 的 Deployment Target 是否都兼容 iOS 10。
- 方法签名是否与 SDK 声明完全一致,不要自行修改 selector。
- 每个 completion block 是否只调用一次,并且在所有分支中都会调用。
- Intent Extension 是否在异步操作完成前退出、超时或崩溃。
如果确认方法也没有调用
问题描述中同时提到了“confirmSendPayment 能调用”和“confirmSendPayment 未调用”,这两种情况需要分开排查。
如果解析方法执行后,confirmSendPayment:completion: 始终没有进入,应先检查解析结果。返回以下任一结果时,系统都不会直接进入确认阶段:
[INPersonResolutionResult needsValue]
[INPersonResolutionResult unsupported]
[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:...]
金额解析遵循同样的规则。所有参数最终都返回成功结果后,系统才会继续执行:
[INPersonResolutionResult successWithResolvedPerson:payee]
[INCurrencyAmountResolutionResult
successWithResolvedCurrencyAmount:amount]
还应在 Extension 的所有入口处设置断点或日志,检查是否有分支遗漏 completion、发生异常退出,或者方法签名不匹配。
注意事项
confirmSendPayment 只用于检查支付条件,例如账户状态、余额、币种和收款人是否有效。真正的支付操作应放在 handleSendPayment 中。不要在确认阶段完成扣款,因为 Siri 可能重复确认,也可能中止流程。
如果业务要求用户进入主 App 完成额外的身份验证,可以返回 INSendPaymentIntentResponseCodeFailureRequiringAppLaunch。此时 Siri 提示用户在 App 中继续,属于预期行为。如果希望整个支付流程都在 Siri 中完成,确认阶段必须返回 Ready,同时确保 Extension 能在系统规定的时间内完成处理。
备注:内容仅供参考。
