React Native与iOS交互汇总(无废话,纯干货)

前言

最近用RN开发SDK,涉及RN与iOS各种交互。

有些交互比如用iOS原生切换多个RN页面,以及iOS调用RN的方法,按照网上的方法调不通,一度不知如何是好,网上资料比较少。

于是自己看RN源码分析得出一些方法。
如有问题欢迎指正,有更好的思路方法欢迎分享。

目录

一、 iOS 调用React Native
1,打开一个React Native页面
2,多个React Native页面切换(尽量在RN内实现)
3,iOS调用RN(分是否传参数)
二、React Native调用iOS
1,无参数无回调
2,有多个参数
3,有回调
4,有多个参数多个回调

说明:

1,Demo: RNInteractionWithIOS

  • demo截图:


    Snip20190319_1.png

2,React Native版本:

"react": "16.4.1",
"react-native": "0.56.0"

一、 iOS 调用React Native

1,打开一个React Native页面

比如react-native init RNInteractionWithiOS 创建一个应用之后就会自动在 RNInteractionWithiOS->ios->RNInteractionWithiOS->AppDelegate.m中生成打开一个React Native页面的代码。核心代码如下:

 NSURL *jsCodeLocation;

 jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];

 RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                     moduleName:@"RNInteractionWithiOS"
                                              initialProperties:nil
                                                  launchOptions:launchOptions];
 rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

 self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
 UIViewController *rootViewController = [UIViewController new];
 rootViewController.view = rootView;
 self.window.rootViewController = rootViewController;
 [self.window makeKeyAndVisible];

2,多个React Native页面切换(尽量在RN内实现)

这个有点难度,当时还研究了半天,几乎没有资料可参考

  • RN核心代码:
    在index.js中
AppRegistry.registerComponent("App", () => App);
AppRegistry.registerComponent("App2", () => App2);
  • iOS中OC核心代码:
    • 设置RCTBridge的代理
    • 实现代理方法- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge;
    • 最关键的:通常情况下我们会使用initWithBundleURL创建一个RCTRootView,此处必须使用initWithBridge创建RCTRootView
#import "ViewController.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

@interface ViewController ()<RCTBridgeDelegate>
@property (nonatomic, strong) RCTBridge *bridge;

@end

@implementation ViewController

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
  return [NSURL URLWithString:@"http://127.0.0.1:8081/index.bundle?platform=ios"];// 模拟器
}

- (void)viewDidLoad {
    [super viewDidLoad];
  _bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:nil];
}

#define RNBounds CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width-50, UIScreen.mainScreen.bounds.size.height/2.f)

- (IBAction)openRNOne {
  [self removeRNPage];
  [self loadRNView:@"App"];
}

- (IBAction)openRNTwo {
  [self removeRNPage];
  [self loadRNView:@"App2"];
}

- (IBAction)removeRNPage {
  [self.view.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    if ([obj isKindOfClass:[RCTRootView class]]) {
      [obj removeFromSuperview];
      return;
    }
  }];
}

- (void)loadRNView:(NSString *)moduleName {
  RCTRootView *rnView2 = [[RCTRootView alloc] initWithBridge:_bridge
                                                  moduleName:moduleName
                                           initialProperties:nil];
  rnView2.bounds = RNBounds;
  rnView2.center = self.view.center;
  [self.view addSubview:rnView2];
}

3,iOS调用RN(分是否传参数)

  • RN核心代码
  componentWillMount() {
      DeviceEventEmitter.addListener('EventInit', (msg) => {
          let receive = "EventInit: " + msg;
          console.log(receive);
          this.setState({notice: receive});
      });

      DeviceEventEmitter.addListener('EventLogin', (msg) => {
          let receive = "EventLogin: " + msg;
          console.log(receive);
          this.setState({notice: receive});
      });
  }
  • iOS核心代码:
    • 创建的iOS交互类要引用#import<React/RCTBridgeModule.h>#import <React/RCTEventEmitter.h>,继承RCTEventEmitter,并遵守RCTBridgeModule
    • 很关键的:交互类要设置bridge当前RCTRootView的bridge[[ReactInteraction shareInstance] setValue:rnView.bridge forKey:@"bridge"];

ReactInteraction.h文件

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

@interface ReactInteraction : RCTEventEmitter <RCTBridgeModule>
+ (instancetype)shareInstance;
- (void)init:(NSString *)parameter;
- (void)login;
@end

ReactInteraction.m文件


#import "ReactInteraction.h"

#define INIT @"EventInit"
#define LOGIN @"EventLogin"

@implementation ReactInteraction
static ReactInteraction *instance = nil;

RCT_EXPORT_MODULE();
+ (instancetype)shareInstance {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    instance = [[self alloc] init];
  });
  return instance;
}

- (NSArray<NSString *> *)supportedEvents
{
  return @[INIT,LOGIN];
}

RCT_EXPORT_METHOD(init:(NSString *)msg) {
  [self.bridge enqueueJSCall:@"RCTDeviceEventEmitter"
                      method:@"emit"
                        args:@[INIT, msg]
                  completion:NULL];
}

RCT_EXPORT_METHOD(login) {
  [self.bridge enqueueJSCall:@"RCTDeviceEventEmitter"
                      method:@"emit"
                        args:@[LOGIN]
                  completion:NULL];
}

@end

二、React Native调用iOS

  • RN核心代码:
import {NativeModules} from 'react-native';
const NativeInteraction = NativeModules.NativeInteraction;
  • iOS核心代码:
    • 注意点1:RCT_EXPORT_METHODRCT_REMAP_METHOD宏定义的使用区别(个人总结,有不对请指正)
      • RCT_EXPORT_METHOD:用于仅有一个参数或回调
      • RCT_REMAP_METHOD:用于有多个参数或(和)多个回调
        (了解更多可以看RN宏定义源码1,下面贴出关键两句)
    • 注意点2:iOS回调方式有两种
      • callback(@[jsonString]); ((RCTPromiseResolveBlock)resolver)
      • Promise方式:_resolveBlock(@[jsonString]); ((RCTResponseSenderBlock)callback) (了解更多看RN源码2

源码1:

#define RCT_REMAP_METHOD(js_name, method) \
  _RCT_EXTERN_REMAP_METHOD(js_name, method, NO) \
  - (void)method RCT_DYNAMIC;

#define RCT_EXPORT_METHOD(method) \
  RCT_REMAP_METHOD(, method) 

源码2:

typedef void (^RCTResponseSenderBlock)(NSArray *response);
typedef void (^RCTResponseErrorBlock)(NSError *error);
typedef void (^RCTPromiseResolveBlock)(id result);
typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);

1,无参数回调

  • RN核心代码:
    NativeInteraction.RNTransferIOS();
  • iOS核心代码:
RCT_EXPORT_METHOD(RNTransferIOS) {
  NSLog(@"RN调用iOS");
}

2,有多个参数

  • RN核心代码:
 NativeInteraction.RNTransferIOSWithParameter('{\'para1\':\'rndata1\',\'para2\':\'rndata2\'}');
  • iOS核心代码:
RCT_EXPORT_METHOD(RNTransferIOSWithParameter:(NSString *)logString) {
  NSLog(@"来自RN的数据:%@",logString);
}

3,有回调

  • RN核心代码:
    NativeInteraction.RNTransferIOSWithCallBack((data) => {
      this.setState({notice: data});
    });
  • iOS核心代码:
RCT_EXPORT_METHOD(RNTransferIOSWithCallBack:(RCTResponseSenderBlock)callback) {
  callback(@[[NSString stringWithFormat:@"来自iOS Native的数据:%@",TestNativeJsonData]]);
}

4,有多个参数多个回调

  • RN核心代码:
 NativeInteraction.RNTransferIOSWithParameterAndCallBack('rndata1','rndata2').then(result =>{
      let jsonString = JSON.stringify(result);
      this.setState({notice: jsonString});
    }).catch(error=>{
    });
  • iOS核心代码:
{
  RCTPromiseResolveBlock _resolveBlock;
  RCTPromiseRejectBlock _rejectBlock;
}
RCT_REMAP_METHOD(RNTransferIOSWithParameterAndCallBack, para1:(NSString *)para1 para2:(NSString *)para2 resolver:(RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)reject) {
  NSLog(@"来自RN的数据:para1——%@, para2——%@",para1, para2);

  _resolveBlock=resolver;
  _rejectBlock=reject;
  NSString *jsonString = TestNativeJsonData;
  _resolveBlock(@[jsonString]);
}

如若需要完整代码,请看我写的一个小Demo: RNInteractionWithIOS

如上有问题欢迎各路大神留言指正,写博客不容易,大家相互学习。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容