ReactNative与iOS的交互

本文详述React Native (RN)与iOS原生代码之间的交互机制,包括RCTRootView初始化策略、创建自定义RNBridgeManager、从原生调用RN页面、RN调用原生方法、回调机制、事件通知等核心内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文简要展示RN与iOS原生的交互功能。

1.1 RCTRootView初始化问题

/**
 * - Designated initializer -
 */
- (instancetype)initWithBridge:(RCTBridge *)bridge
                    moduleName:(NSString *)moduleName
             initialProperties:(NSDictionary *)initialProperties NS_DESIGNATED_INITIALIZER;

/**
 * - Convenience initializer -
 * A bridge will be created internally.
 * This initializer is intended to be used when the app has a single RCTRootView,
 * otherwise create an `RCTBridge` and pass it in via `initWithBridge:moduleName:`
 * to all the instances.
 */
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
                       moduleName:(NSString *)moduleName
                initialProperties:(NSDictionary *)initialProperties
                    launchOptions:(NSDictionary *)launchOptions;
复制代码

1、当Native APP内只有一处RN的入口时,可以使用initWithBundleURL,否则的话就要使用initWithBridge方法。
2、因为initWithBundleURL会在内部创建一个RCTBridge,当有多个RCTRootView入口时,就会存在多个RCTBridge,容易导致Native端与RN交互时多次响应,出现BUG。

1.2 创建自定义的RNBridgeManager

由于APP内有RN多入口的需求,所以共用一个RCTBridge

RNBridgeManager.h


#import <Foundation/Foundation.h>
#import <React/RCTBridge.h>

NS_ASSUME_NONNULL_BEGIN

@interface RNBridgeManager : RCTBridge
/**
 RNBridgeManager单例
 */
+ (instancetype)sharedManager;

@end

NS_ASSUME_NONNULL_END

复制代码

RNBridgeManager.m

#import "RNBridgeManager.h"

#import <React/RCTBundleURLProvider.h>

//dev模式下:RCTBridge required dispatch_sync to load RCTDevLoadingView Error Fix
#if RCT_DEV
#import <React/RCTDevLoadingView.h>
#endif
/**
 自定义类,实现RCTBridgeDelegate
 */
@interface BridgeHandle : NSObject<RCTBridgeDelegate>

@end

@implementation BridgeHandle

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge{
    return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
}
@end


@implementation RNBridgeManager

+ (instancetype)sharedManager{
    static RNBridgeManager *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[RNBridgeManager alloc] initWithDelegate:[[BridgeHandle alloc] init] launchOptions:nil];
#if RCT_DEV
        [manager moduleForClass:[RCTDevLoadingView class]];
#endif
    });
     return manager;
}


@end

复制代码

1.3 Native进入RN页面

 RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:[RNBridgeManager sharedManager] moduleName:@"RNTest" initialProperties:nil];
 UIViewController *vc = [[UIViewController alloc] init];
 vc.view = rootView;
 [self.navigationController pushViewController:vc animated:YES];
复制代码

1.4 RN调用Native方法

  • 创建一个交互的类,实现<RCTBridgeModule>协议;
  • 固定格式:在.m的实现中,首先导出模块名字RCT_EXPORT_MODULE();RCT_EXPORT_MODULE接受字符串作为其Module的名称,如果不设置名称的话默认就使用类名作为Modul的名称;
  • 使用RCT_EXPORT_METHOD导出Native的方法;
1.4.1 比如我们导出Native端的SVProgressHUD提示方法:

RNInterractModule.h

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>

NS_ASSUME_NONNULL_BEGIN

@interface RNInterractModule : NSObject<RCTBridgeModule>

@end

NS_ASSUME_NONNULL_END

复制代码

RNInterractModule.m

import "RNInterractModule.h"
#import "Util.h"
#import <SVProgressHUD.h>

@implementation RNInterractModule
////RCT_EXPORT_MODULE接受字符串作为其Module的名称,如果不设置名称的话默认就使用类名作为Modul的名称
RCT_EXPORT_MODULE();

//==============1、提示==============
RCT_EXPORT_METHOD(showInfo:(NSString *) info){
    dispatch_sync(dispatch_get_main_queue(), ^{
        [SVProgressHUD showInfoWithStatus:info];
    });
}
@end

复制代码
1.4.2 RN端调用导出的showInfo方法:

我们在RN端把Native的方法通过一个共同的utils工具类引入,如下


import { NativeModules } from 'react-native';

//导出Native端的方法
export const { showInfo} = NativeModules.RNInterractModule;
复制代码

具体的RN页面使用时:

import { showInfo } from "../utils";

//通过Button点击事件触发
<Button
	title='1、调用Native提示'
	onPress={() => showInfo('我是原生端的提示!')}
/>
复制代码

调用效果:

1.4.3 RN回调Native

RN文档显示,目前iOS端的回调还处于实验阶段

我们提供一个例子来模拟:目前的需求是做面包,RN端能提供面粉,但是不会做,Native端是有做面包的功能;所以我们需要先把面粉,传给Native端,Native加工好面包之后,再通过回调回传给RN端。

Native端提供方法

// 比如调用原生的方法处理图片、视频之类的,处理完成之后再把结果回传到RN页面里去
//TODO(RN文档显示,目前iOS端的回调还处于实验阶段)
RCT_EXPORT_METHOD(patCake:(NSString *)flour successBlock:(RCTResponseSenderBlock)successBlock errorBlock:(RCTResponseErrorBlock)errorBlock){
    __weak __typeof(self)weakSelf = self;
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSString *cake = [weakSelf patCake:flour];
        //模拟成功、失败的block判断
        if([flour isKindOfClass:[NSString class]]){
            successBlock(@[@[cake]]);//此处参数需要放在数组里面
        }else{
            NSError *error = [NSError errorWithDomain:@"com.RNTest" code:-1 userInfo:@{@"message":@"类型不匹配"}];
            errorBlock(error);
        }
    });
}


//使用RN端传递的参数字符串:"",调用Native端的做面包方法,加工成面包,再回传给RN
- (NSString *)patCake:(NSString *)flour{
    NSString * cake = [NSString stringWithFormat:@"使用%@,做好了:??????",flour];
    return cake;
}
复制代码

RN端调用:

//首先工具类里先引入
export const { showInfo,patCake } = NativeModules.RNInterractModule;


//具体页面使用
<Button
	title='4、回调:使用面粉做蛋糕'
	onPress={() => patCake('1斤面粉',
		(cake) => alert(cake),
		(error) => alert('出错了' + error.message))}
	/>
复制代码

调用效果:

1.4.4 使用Promise回调

Native端提供方法


RCT_EXPORT_METHOD(callNameTointroduction:(NSString *)name resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock) reject){
    __weak __typeof(self)weakSelf = self;
    dispatch_sync(dispatch_get_main_queue(), ^{
        if ([name isKindOfClass:NSString.class]) {
            resolve([weakSelf introduction:name]);
        }else{
            NSError *error = [NSError errorWithDomain:@"com.RNTest" code:-1 userInfo:@{@"message":@"类型不匹配"}];
            reject(@"class_error",@"Needs NSString Class",error);
        }
    });
}

- (NSString *)introduction:(NSString *)name{
    return [NSString stringWithFormat:@"我的名字叫%@,今年18岁,喜欢运动、听歌...",name];
}
复制代码

RN端调用:

//首先工具类里先引入
export const { showInfo,patCake, callNameTointroduction} = NativeModules.RNInterractModule;

//具体页面使用
<Button
	title='5、Promise:点名自我介绍'
	onPress={
		async () => {
			try {
				let introduction = await callNameTointroduction('小明');
				showInfo(introduction);
			} catch (e) {
				alert(e.message);
			}
		}
	}
/>

复制代码

调用效果:

1.5 Native端发送通知到RN

Native端继承RCTEventEmitter,实现发送RN通知类:

RNNotificationManager.h


#import <Foundation/Foundation.h>
#import <React/RCTEventEmitter.h>
#import <React/RCTBridgeModule.h>

NS_ASSUME_NONNULL_BEGIN

@interface RNNotificationManager : RCTEventEmitter

+ (instancetype)sharedManager;

@end

NS_ASSUME_NONNULL_END

复制代码

RNNotificationManager.m


#import "RNNotificationManager.h"

@implementation RNNotificationManager
{
    BOOL hasListeners;
}


+ (instancetype)sharedManager{
    static RNNotificationManager *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        manager = [[self alloc] init];
    });
    return manager;
}

- (instancetype)init{
    self = [super init];
    if (self) {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center removeObserver:self];
        [center addObserver:self selector:@selector(handleEventNotification:) name:@"kRNNotification_Login" object:nil];
        [center addObserver:self selector:@selector(handleEventNotification:) name:@"kRNNotification_Logout" object:nil];
    };
    return self;
}


RCT_EXPORT_MODULE()

- (NSArray<NSString *> *)supportedEvents{
    return @[
             @"kRNNotification_Login",
             @"kRNNotification_Logout"
             ];
}
//优化无监听处理的事件
//在添加第一个监听函数时触发
- (void)startObserving{
    //setup any upstream listenerse or background tasks as necessary
    hasListeners = YES;
    NSLog(@"----------->startObserving");
}

//will be called when this mdules's last listener is removed,or on dealloc.
- (void)stopObserving{
    //remove upstream listeners,stop unnecessary background tasks.
    hasListeners = NO;
    NSLog(@"----------->stopObserving");
}

+ (BOOL)requiresMainQueueSetup{
    return YES;
}

- (void)handleEventNotification:(NSNotification *)notification{
    if (!hasListeners) {
        return;
    }
    
    NSString *name = notification.name;
    NSLog(@"通知名字-------->%@",name);
    [self sendEventWithName:name body:notification.userInfo];
    
}

@end

复制代码

RN端注册监听:

//utils工具类中导出
export const NativeEmitterModuleIOS = new NativeEventEmitter(NativeModules.RNNotificationManager);


//具体页面使用
import { NativeEmitterModuleIOS } from "../utils";

export default class ActivityScene extends Component {

	constructor(props) {
		super(props);
		this.subscription = null;
		this.state = {
			loginInfo: '当前未登录',
		};
	}

	updateLoginInfoText = (reminder) => {
		this.setState({loginInfo: reminder.message})
	};

	//添加监听
	componentWillMount() {
		this.subscription = NativeEmitterModuleIOS.addListener('kRNNotification_Login', this.updateLoginInfoText);
		
	}
	//移除监听
	componentWillUnmount() {
			console.log('ActivityScene--------->', '移除通知');
			this.subscription.remove();
	}
	render() {
		return (
			<View style={{flex: 1, backgroundColor: 'white'}}>
				<Button
					title='3、RN Push到Native 发送通知页面'
					onPress={() => pushNative(RNEmitter)}
				/>
				<Text style={{fontSize: 20, color: 'red', textAlign: 'center',marginTop:50}}>{this.state.loginInfo}</Text>
			</View>
		);
	}
}
复制代码

效果展示:

1.6 完整Demo(包含iOS & Android)

RN-NativeTest

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值