You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS React Native中Objective-C闭包传递至JavaScript的实现问询

当然可以在 Objective-C 里构造能被 JavaScript 调用的闭包,但在 React Native 桥接场景下——尤其是你给 tipsi-stripe 扩展 Apple Pay 配送/联系人修改支持的场景——很容易因为闭包生命周期、桥接封装的问题导致 JS 端拿到 null。我帮你梳理下核心解决思路和具体实现步骤:

问题根源拆解

React Native 的 Objective-C 桥接对回调的格式、生命周期有严格要求:

  • 如果原生端的闭包没有被正确保留,或者没有按 RN 规范封装成可序列化的回调类型,JS 端就会收到 null
  • Apple Pay 的 PKPaymentAuthorizationViewControllerDelegate 回调(比如配送方式、联系人变更)是异步触发的,需要把原生的 completion block 暂存,等 JS 处理完结果后再触发,这一步很容易出错

具体实现方案

1. 暂存原生 Completion Block

首先在你的扩展模块(比如 StripeApplePayModule.m)里添加属性,用来保存 Apple Pay 回调的 completion block,避免它被提前释放:

// 头文件或实现文件里定义属性
@property (nonatomic, copy) void (^shippingMethodCompletion)(PKPaymentAuthorizationStatus, NSArray<PKShippingMethod *> *);
@property (nonatomic, copy) void (^contactCompletion)(PKPaymentAuthorizationStatus);

2. 转发原生事件到 JS

在 Apple Pay 的 delegate 方法中,暂存 completion block,然后通过 RN 的事件发射器把事件传递给 JS:

// 处理配送方式变更
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectShippingMethod:(PKShippingMethod *)shippingMethod completion:(void (^)(PKPaymentAuthorizationStatus status, NSArray<PKShippingMethod *> *shippingMethods))completion {
    // 暂存 completion block
    self.shippingMethodCompletion = completion;
    
    // 把配送方式转成 JS 可识别的字典(自己实现转换逻辑)
    NSDictionary *shippingMethodDict = [self convertPKShippingMethodToDict:shippingMethod];
    
    // 发送事件到 JS
    [self sendEventWithName:@"applePayShippingMethodSelected" body:shippingMethodDict];
}

// 处理联系人变更
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectContact:(PKContact *)contact completion:(void (^)(PKPaymentAuthorizationStatus status))completion {
    self.contactCompletion = completion;
    NSDictionary *contactDict = [self convertPKContactToDict:contact];
    [self sendEventWithName:@"applePayContactSelected" body:contactDict];
}

3. JS 端处理并回调原生

在 React Native 代码里监听事件,处理完业务逻辑后调用原生暴露的方法触发 completion:

import { NativeEventEmitter, NativeModules } from 'react-native';
const { Stripe } = NativeModules;
const stripeEmitter = new NativeEventEmitter(Stripe);

// 监听配送方式变更事件
stripeEmitter.addListener('applePayShippingMethodSelected', async (shippingMethod) => {
  // 这里处理你的业务逻辑,比如计算新运费、更新配送选项
  const updatedShippingMethods = await yourShippingCalculationLogic(shippingMethod);
  
  // 调用原生方法完成回调
  await Stripe.completeShippingMethodSelection({
    status: 'success', // 或 'failure'
    shippingMethods: updatedShippingMethods
  });
});

// 监听联系人变更事件
stripeEmitter.addListener('applePayContactSelected', async (contact) => {
  // 验证联系人信息等逻辑
  const isValid = await yourContactValidationLogic(contact);
  
  await Stripe.completeContactSelection({
    status: isValid ? 'success' : 'failure'
  });
});

4. 原生端接收 JS 回调并触发 Completion

在 Objective-C 里暴露方法,接收 JS 的处理结果,然后调用之前暂存的 completion block:

// 完成配送方式变更回调
RCT_EXPORT_METHOD(completeShippingMethodSelection:(NSDictionary *)params) {
    if (!self.shippingMethodCompletion) return;
    
    // 转换 JS 传递的状态
    PKPaymentAuthorizationStatus status = [params[@"status"] isEqualToString:@"success"] 
        ? PKPaymentAuthorizationStatusSuccess 
        : PKPaymentAuthorizationStatusFailure;
    
    // 转换 JS 传递的配送方式数组
    NSArray<PKShippingMethod *> *shippingMethods = [self convertDictArrayToPKShippingMethods:params[@"shippingMethods"]];
    
    // 触发原生 completion
    self.shippingMethodCompletion(status, shippingMethods);
    // 调用后清空 block,避免循环引用
    self.shippingMethodCompletion = nil;
}

// 完成联系人变更回调
RCT_EXPORT_METHOD(completeContactSelection:(NSDictionary *)params) {
    if (!self.contactCompletion) return;
    
    PKPaymentAuthorizationStatus status = [params[@"status"] isEqualToString:@"success"] 
        ? PKPaymentAuthorizationStatusSuccess 
        : PKPaymentAuthorizationStatusFailure;
    
    self.contactCompletion(status);
    self.contactCompletion = nil;
}

关键注意事项

  • 循环引用规避:调用完 completion block 后必须把属性置为 nil,否则 block 会持有 self,导致内存泄漏
  • 数据序列化:确保 PKShippingMethod、PKContact 和 JS 之间的转换逻辑正确,所有传递的数据必须是 RN 桥接支持的类型(字典、数组、字符串等)
  • 生命周期管理:确保 PKPaymentAuthorizationViewController 的 delegate 不会被提前释放,否则回调会失效
  • 调试技巧:在原生端加 NSLog 检查 completion block 是否被正确保存,JS 端打印事件参数确认数据是否正常接收

内容的提问来源于stack exchange,提问作者Paul Spencer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:50:56