React Native 与iOS的通信

RN可以很好的与原生进行交互,我们首先看看效果吧:

首先我们来看看React Native 怎样调用iOS的代码并且带有简单的参数:

在iOS工程里面我们新建一个类iOSExport,iOSExport将会实现RCTBridgeModule协议。

首先我们要在iOSExport类的实现中添加这句宏定义:RCT_EXPORT_MODULE()

RCT_EXPORT_MODULE()如果你不传入参数,那么你在iOS中导出的模块名就是类名,你也可以插入参数作为自定义模块名。

@implementation iOSExport
//定义导出的模块名
RCT_EXPORT_MODULE()
@end

后面我们就可以实现协议的代理方法了,协议方法的实现需要在RCT_EXPORT_METHOD,这个宏里面。

我们先写一个有两个参数的方法给js调用:

@implementation iOSExport
//定义导出的模块名
RCT_EXPORT_MODULE()

//定义导出的方法名
RCT_EXPORT_METHOD(rnToiOS:(NSString *)name :(NSInteger)age) {
  NSString *st = [NSString stringWithFormat:@"name:%@,age:%ld",name,age];
    NSLog(@"test:%@",st);
    [self alter:st];
}
@end

这样OC端的工作就OK了,下面我们继续看看js端怎么调用:

首先我们要在js文件里面 import NativeModules

然后在我们需要使用的时候获取导出的模块,我们再用模块调用iOS的导出的函数名就可以了,看代码:

//创建一个可以点击的按钮,点击按钮后调用iOS的rnToiOS方法
<TouchableHighlight
    style={[styles.highLight,{marginTop:50}]}
    underlayColor=‘#deb887‘
    activeOpacity={0.8}
    onPress={() => this._nameAndAge()}
    >
    <Text>简单数据传递</Text>
</TouchableHighlight>

_nameAndAge() { //多参数的传递
        var iOSExport = NativeModules.iOSExport //获取到模块
        iOSExport.rnToiOS(‘帝君‘,200) //直接调用函数
        this.setState({
            text:‘rnToiOS‘
        })
}

下面我们再看如何在js端调用iOS的含有字典参数和回调函数的方法。iOS提供给js的回调函数是使用block实现的,看下回调函数的说明:

/**
 * The type of a block that is capable of sending a response to a bridged
 * operation. Use this for returning callback methods to JS.
 */
typedef void (^RCTResponseSenderBlock)(NSArray *response);

下面我们就可以用回调函数做参数,写一个我们需要的方法:

RCT_EXPORT_METHOD(rnToiOSwithDic:(NSDictionary*)dic andCallback:(RCTResponseSenderBlock)callback) {
  NSMutableString *st = [NSMutableString string];
  for (NSObject *key in dic.allKeys) {
    NSString *string = [NSString stringWithFormat:@"%@:%@;",key,[dic objectForKey:key]];
     [st appendString:string];
  }
  callback(@[@"error",st]);
  [self alter:st];
}

最终我们的回调函数给js的是一个数组,一般这个数组的第一个元素表示的都是错误。

看下如何在js端调用这个方法:

//为了测试方便我们先写个按钮
<TouchableHighlight
    style={styles.highLight}
    underlayColor=‘coral‘
    activeOpacity={0.8}
    onPress={() => this._dic()}
    >
    <Text>字典的传递和回调</Text>
</TouchableHighlight>

//字典的传递和返回值
    _dic() {
        var iOSExport = NativeModules.iOSExport //获取导出的模块
        iOSExport.rnToiOSwithDic({ //调用iOS的方法,第一个参数是字典
            ‘姓名‘:‘幽冥‘,
            ‘年龄‘:20,
            ‘法力‘:‘200‘
        },(error,strings) =>{ //第二个参数是函数,做为回调函数给iOS将由iOS调用

            this.setState({
                text:strings
            })
        })
        this.setState({
            text:‘rnToiOSwithDic‘
        })
    }

上面对于回调函数的处理稍显麻烦,我们再看使用promise实现的回调函数:

我们先看下iOS里面的两个block:

/**
 * Block that bridge modules use to resolve the JS promise waiting for a result.
 * Nil results are supported and are converted to JS‘s undefined value.
 */
typedef void (^RCTPromiseResolveBlock)(id result);

/**
 * Block that bridge modules use to reject the JS promise waiting for a result.
 * The error may be nil but it is preferable to pass an NSError object for more
 * precise error messages.
 */
typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);

这两个block其实就是实现promise回调的关键,一个是成功的回调一个是失败的回调,看下iOS端的实现:

RCT_EXPORT_METHOD(rnToiOSAge:(NSInteger)age resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  if (age > 23) {
    resolve(@[@"句芒"]);
  }else {
    reject(@"101",@"年龄错误",[NSError errorWithDomain:@"错误" code:1 userInfo:nil]);
  }
}

在js里面我要调用这个方法其实只要传一个参数age就可以了,后面两个参数可能会转换为promis作为返回值处理,看下js端是怎么调用的:

//作为测试的按钮
<TouchableHighlight
    style={styles.highLight}
    underlayColor=‘#5f9ea0‘
    activeOpacity={0.8}
    onPress={() => this._promise(30)}
    >
    <Text>Promise回调</Text>
</TouchableHighlight>
//按钮的回调事件,将在这里调用iOS的方法
async _promise(age) { //Promise回调,异步执行
    try{
            var iOSExport = NativeModules.iOSExport
            var resolve = await iOSExport.rnToiOSAge(age) //执行iOS函数并且等待结果
            this.setState({
                text:resolve
            })
    }catch(e) {
            console.error(e);
    }
}

这里要说下,因为我们是采用promsie返回值的方式处理回调的,所以我们不知道何时将返回结果,所以按钮的点击函数_promise(age)前面要加个async关键字标识为异步函数,我们在函数里面可以使用try{}catch(){}来捕获异常,因为我们明确的知道如果age小于24就将报错,所以我们在这里一定要添加异常处理。注意了在执行iOS函数的时候也要加await关键字的,等待获取到返回值后再执行下面的操作。

在iOS里面也可以很方便地给js注入常量,采用如下方法可以方便的提供常量:

/**
 * Injects constants into JS. These constants are made accessible via
 * NativeModules.ModuleName.X.  It is only called once for the lifetime of the
 * bridge, so it is not suitable for returning dynamic values, but may be used
 * for long-lived values such as session keys, that are regenerated only as
 * part of a reload of the entire React application.
 */
- (NSDictionary<NSString *, id> *)constantsToExport;

这里要注意了,这函数只会在桥接的过程中执行一次,所以不太适合变量的传递,iOS里面的实现:

//为js提供静态数据
- (NSDictionary<NSString *,id> *)constantsToExport {
  return @{@"name":@"闲",@"age":@"22"};
}

js如何调用的:

<TouchableHighlight
    style={styles.highLight}
    underlayColor=‘#5f9ea0‘
    activeOpacity={0.8}
    onPress={() => this._getConst()}
    >
    <Text>获取iOS常量</Text>
</TouchableHighlight>

_getConst() {
    var iOSExport = NativeModules.iOSExport //获取模块
    this.setState({
        text:iOSExport.name+‘,‘+iOSExport.age //获取常量
    })
}

在上面这些函数里面如果我想统一指定他们在什么线程里执行的,只要实现这函数就可以了:

//告诉程序这个模块的代码在哪个线程执行
- (dispatch_queue_t)methodQueue {
  return dispatch_get_main_queue(); //返回一个指定的线程
}

前面说了那么多都是怎么在js里面调用iOS的方法的,那么如何让iOS主动发送消息给js呢。

首先我们更改一下iOSExport这个类,让它继承自RCTEventEmitter,并且删除协议,因为RCTEventEmitter这个类也会实现RCTBridgeModule协议的:

//继承自RCTEventEmitter的OC类将有资格给js发送消息
@interface iOSExport :RCTEventEmitter
@end

然后在iOSExport的实现里面我们还要重写一个方法,用来指定这模块将会发送哪些消息给js :

- (NSArray<NSString *> *)supportedEvents {
  return @[@"sendName"]; //这里返回的将是你要发送的消息名的数组。
}

然后我们就可以直接发送消息了:

- (void)alter:(NSString *)st {
  UIAlertController *alter = [UIAlertController alertControllerWithTitle:@"测试" message:st preferredStyle:UIAlertControllerStyleAlert];

  [alter addAction:[UIAlertAction actionWithTitle:@"了解" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    //iOS发送通知给js
    [self sendEventWithName:@"sendName" body:@{@"name":@"江山",@"age":@"5000"}];
  }]];
  [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alter animated:YES completion:nil];
}

我们使用- (void)sendEventWithName:(NSString *)eventName body:(id)body 发送消息,eventName将是消息的名字,body可以传个字典作为消息体。

那么如何在js里面接受消息呢:

首先要import NativeEventEmitter,然后我们注册监听事件:

    componentWillMount() {
        //开始监听
        var iOSExport = NativeModules.iOSExport
        var emitter = new NativeEventEmitter(iOSExport) //用获取的模块创建监听器
        this.subScription = emitter.addListener("sendName",(body) => this._getNotice(body)) //监听指定的事件,通过sendName这事件名来识别事件,(body) => this._getNotice(body)这是监听到事件后的处理方法,body 是iOS传过来的消息体
}

_getNotice (body) {
    this.setState({
        notice:body.name+‘,‘+body.age
    })
}

最后我们要注销监听:

componentWillUnmount() {
    //删除监听
    this.subScription.remove()
}

以上源码都已经上传gitub,源码下载

时间: 2024-08-28 18:34:08

React Native 与iOS的通信的相关文章

React Native的iOS开发步骤以及崩溃收集

React Native的iOS开发以及崩溃收集 简介 React Native使你能够在Javascript和React的基础上获得完全一致的开发体验,构建世界一流的原生APP. React Native着力于提高多平台开发的开发效率 -- 仅需学习一次,编写任何平台.(Learn once, write anywhere) Facebook已经在多项产品中使用了React Native,并且将持续地投入建设React Native. 准备 安装 iOS只能MAC下开发,需要Xcode; An

React Native移植iOS原生项目

手工集成React Native 第一步:初始化React Native环境 在我们要集成的项目中,进入到*.xcodeproj文件的上级目录,运行React Native初始化命令react-native init [Project Name]会出现prompt, 输入yes,这样会在ios目录下生成一个同名工程.init过程会需要一点时间,耐心等待. 完成后项目文件目录会变成这样: node_modules:react native依赖包 ios:iOS项目相关代码,Xcode工程文件 an

react native 使用 iOS 原生 UI 组件

目前 react native 的组件还是不多,有些也并不怎么好用,这时候就需要封装原生 UI 组件了 之前写过RN 与 native 的通信 无非就是两种: 1>>>  react native 内部事件需要通知 native 调用 native 的方法(或者传递RN 中的数据到 native),这时候可以用新建一个 manager 之类的文件 RCT_EXPORT_MOUDLE() 暴露 native类  ,RCT_EXPORT_METHOD () 暴露 native 方法给 js

react native 开发IOS

转载自kaich blog(http://www.kaich.xyz) 接触 react native 对于技术,我比较喜欢追新.看到报道大名鼎鼎的facebook(开源界的模范,发布了很多高质量的开源框架)开源了移动端跨平台的新框架react native,于是就迫不及待的开始接触它了.react native的目的是为了让前端开发任务能开发移动端(ios程序员相对于web端来说要少得多),于是就有了这样的框架.对于我们IOS程序员有没有必要去学它,看到它另外一个特性:跨平台,而且又一定程度的

[React Native] Basic iOS Routing -- NavigatorIOS

Inside the app component, we use NavigatiorIOS to render the compoent: class githubnotetaker extends Component { render() { return ( <NavigatorIOS style={styles.container} initialRoute={{ title: 'Github note taker', component: Main }} /> ); } } This

[React Native混合开发]React Native for iOS之创建第一个案例

一 摘要 上一篇文章,介绍了如何在MAC电脑上搭建React Native运行环境,环境搭建好了,接下来这篇给大家介绍一下如何创建自己的工程项目. 二 具体步骤 第一步 创建HelloWord工程 第三步 运行工程 1.目录结构 2.打开ios目录运行工程文件 这样就打开了工程了 3.Command+R运行工程 第四步 代码编辑 现在大家最关心的在哪里写代码,下面就给大家介绍在哪个位置进行代码的编写. 现在我们以混编iOS为例,打开index.ios.js 我们在这个文件进行代码的编写.如果是混

如何通过 Chrome调试React Native程序-ios

1.下载 New React Developer Tools ,向Chrome添加此扩展程序(浏览器右上角—>设置),启用开发者模式 2.在模拟器下单击”Debug JS Remotely” 启动JS远程调试功能.此时Chrome会被打开,同时会创建一个“http://localhost:8081/debugger-ui.” Tab页. 3.视图—>开发者—>开发者工具—>sources分页即可打开React文件进断点设置

【REACT NATIVE 系列教程之十二】REACT NATIVE(JS/ES)与IOS(OBJECT-C)交互通信

一用到跨平台的引擎必然要有引擎与各平台原生进行交互通信的需要.那么Himi先讲解React Native与iOS之间的通信交互. 本篇主要分为两部分讲解:(关于其中讲解的OC语法等不介绍,不懂的请自行学习) 1. React Native 访问iOS 2. iOS访问React Native     一:React Native 访问iOS 1. 我们想要JS调用OC函数,就要实现一个"RCTBridgeModule"协议的Objective-C类 所以首先我们先创建一个oc新类,  

React Native通信机制详解

本文转载至 http://blog.cnbang.net/tech/2698/?from=groupmessage&isappinstalled=1 React Native是facebook刚开源的框架,可以用javascript直接开发原生APP,先不说这个框架后续是否能得到大众认可,单从源码来说,这个框架源码里有非常多的设计思想和实现方式值得学习,本篇先来看看它最基础的JavaScript-ObjectC通信机制(以下简称JS/OC). 概览 React Native用iOS自带的Java