一用到跨平臺的引擎必然要有引擎與各平臺原生進行交互通信的需要。那么Himi先講解React Native與iOS之間的通信交互。
本篇主要分為兩部分講解:(關于其中講解的OC語法等不介紹,不懂的請自行學習)
1. React Native 訪問iOS
2. iOS訪問React Native
一:React Native 訪問iOS
1. 我們想要JS調用OC函數,就要實現一個“RCTBridgeModule”協議的Objective-C類
所以首先我們先創建一個oc新類, Himi這里起名為:TestOJO (O: object-c, J: javaScript )
2. TestOJO.h
| 123456 | #import <Foundation/Foundation.h>#import "RCTBridgeModule.h" @interface TestOJO : NSObject <RCTBridgeModule> @end | 
引入:#import “RCTBridgeModule.h” 且使用 <RCTBridgeModule> 接口,
3. 為了實現RCTBridgeModule協議,類需要包含RCT_EXPORT_MODULE()宏(這個宏也可以添加一個參數用來指定在Javascript中訪問這個模塊的名字。如果你不指定,默認就會使用這個Objective-C類的名字。)
4. 在TestOJO.m中添加如下:
| 1234567 | RCT_EXPORT_MODULE(); //橋接到Javascript的方法返回值類型必須是void。React Native的橋接操作是異步的,所以要返回結果給Javascript,必須通過回調或者觸發事件來進行RCT_EXPORT_METHOD(j2oFun1:(NSString*)dataString dateNumber:(int)dateNumber){ NSLog(@"js call iOS function j2oFun1/n dataString: %@ |dateNumber :%d",dataString,dateNumber);} | 
想要將oc的函數導出給js進行調用,那么就需要進行聲明。聲明通過RCT_EXPORT_METHOD()宏來實現:
j2oFun1:函數名,后續是兩個參數,分別是NSString 和 int 類型數據。
調用成功后,我們輸出這兩個傳來的值到控制臺。
注意:Javascript調用的OC函數,此函數返回值類型必須是void。由于React Native的橋接操作是異步的,所以要返回結果給Javascript,必須通過回調參數進行 后續詳細講解。
從js傳來的參數我們可以依靠自動類型轉換的特性,跳過手動的類型轉換(RCTConvert,下面詳細介紹),在定義函數參數類型時,直接寫上對應想要的數據類型,例如NSData等。
5. 下面看js調用的代碼段:
| 123 | var TestOJO = require('react-native').NativeModules.TestOJO; TestOJO.j2oFun1('Himi', 12321); | 
var TestOJO=require(‘react-native’).NativeModules.TestOJO;(將OC注冊進來的模塊取出)
TestOJO.j2oFun1(‘Himi’, 12321);(調用模塊中的對應函數,且將參數進行傳入)
6. 我們來看一段復雜的數據通信
OC 代碼段(導出函數):
| 12345678910111213 | #import "RCTConvert.h" RCT_EXPORT_METHOD(j2oFun2:(NSDictionary*)details){ NSString*name=[RCTConvert NSString:details[@"name"]]; NSNumber*age=[RCTConvert NSNumber:details[@"age"]]; NSArray*array=[RCTConvert NSArray:details[@"array"]]; NSLog(@"js call iOS function j2oFun2/n name: %@ | age :%@",name,[agestringValue]); for(inti=0;i<[arraycount];i++){ NSLog(@"array: 第%d個元素:%@",i,array[i]); }} | 
需要注意的是,引入了”RCTConvert”類,作用:
RCTConvert提供了一系列輔助函數,用來接收一個JSON值并轉換到原生Objective-C類型或類。
JS代碼段:(調用OC函數)
| 1234567 | TestOJO.j2oFun2({ name:'Himi', age:12, array:[ 'hi,Himi','i,m','a array!' ] }); | 
7. 我們下面來利用回調參數來得到訪問OC的函數得到其返回值
| 123456 | RCT_EXPORT_METHOD(j2oCallbackEvent:(NSString*)jsString callback:(RCTResponseSenderBlock)callback){ NSLog(@"js call iOS function: j2oCallbackEvent /n jsString:%@",jsString); NSArray*events=[[NSArrayalloc] initWithObjects:@"Himi",@"12321",nil]; callback(@[[NSNullnull],events]);} | 
RCTResponseSenderBlock 是種特殊的參數類型——回調函數,通過此參數可以實現當JS訪問的OC函數后,并能將此OC函數的返回值傳遞給JS。
RCTResponseSenderBlock 只接受一個參數(傳遞給JavaScript回調函數的參數數組)
callback函數:第一個參數是一個錯誤對象(沒有發生錯誤的時候為null),而剩下的部分是函數的返回值。
下面我們來看JS調用代碼段:
| 1234567 | TestOJO.j2oCallbackEvent('Himi',(error,callBackEvents)=>{ if (error) { console.error(error); } else { Alert.alert('J2O帶返回值', '數組的三個值:/n[0]:'+callBackEvents[0]+'/n[1]:'+callBackEvents[1]+'/n[2]:'+callBackEvents[2]); }}); | 
二: iOS訪問React Native
1. 我們如果想要OC訪問JS,給JavaScript發送事件通知,我們需要使用RCTEventDispatcher的函數,與RCTBridge的實例
因此我們需要先做準備,TestOJO.h:
| 123 | #import "RCTEventDispatcher.h" @synthesizebridge=_bridge; | 
bridge: 是RCTBridge 的實例,且在我們使用的接口 RCTBridgeModule中。
OC訪問JS的代碼段:
| 1 | [self.bridge.eventDispatcher sendAppEventWithName:@"eventName" body:@{@"name":@"Himi",@"age": @12}]; | 
第一個參數:事件名
第二個參數(body):傳入的參數
其中@{}是定義不可變的字典的快捷實例方式,因此我們也可以改成如下形式:
| 12 | NSDictionary*direct=@{@"name":@"Himi",@"age":@12}; [self.bridge.eventDispatcher sendAppEventWithName:@"eventName" body:direct]; | 
下面來看JS中定義OC調用的函數:
其實所謂OC能響應JS,是JS進行了對應函數的綁定監聽。因此我們需要利用 NativeAppEventEmitter 組件,利用其addListener進行注冊監聽!因此我們需要引入進來這個模塊,
| 12345678910 | import { ... NativeAppEventEmitter ... } from 'react-native'; var o2cFun = NativeAppEventEmitter.addListener( 'eventName', (para) => Alert.alert('被OC觸發','字典數據:/n name:'+para.name+'/n age:'+para.age)); | 
var o2cFun : 將綁定好的監聽事件引用交給此變量保存。
addListener:
第一個參數:事件名
第二個參數:響應函數
注意:利用addListener進行監聽,一定要對應有取消監聽!要保持一一對應的好習慣。
且通常取消監聽都在componentWillUnmount函數中進行。如下:
| 123 | componentWillUnmount(){ o2cFun.remove(); } | 
其中對于原理并沒有詳細的介紹,這里推薦兩篇文章,童鞋們可以詳細的閱讀一下,這里不贅述:
http://www.jianshu.com/p/203b91a77174
http://reactnative.cn/docs/0.21/native-modules-ios.html#content
下面給出源碼:
TestOJO.h:
| 1234567891011121314 | //// TestOJO.h// MyPRoject//// Created by Himi on 16/6/2.// Copyright © 2016年 Facebook. All rights reserved.// #import <Foundation/Foundation.h>#import "RCTBridgeModule.h" @interface TestOJO : NSObject <RCTBridgeModule> @end | 
TestOJO.m:
| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 | //// TestOJO.m// MyProject//// Created by Himi on 16/6/2.// Copyright ? 2016年 Facebook. All rights reserved.// #import "TestOJO.h" //RCTConvert類支持的的類型也都可以使用,RCTConvert還提供了一系列輔助函數,用來接收一個JSON值并轉換到原生Objective-C類型或類。#import "RCTConvert.h" //本地模塊也可以給JavaScript發送事件通知。最直接的方式是使用eventDispatcher#import "RCTEventDispatcher.h" @implementationTestOJO //====================================[JS -> OC]======================================= RCT_EXPORT_MODULE(); //橋接到Javascript的方法返回值類型必須是void。React Native的橋接操作是異步的,所以要返回結果給Javascript,必須通過回調或者觸發事件來進行RCT_EXPORT_METHOD(j2oFun1:(NSString*)dataString dateNumber:(int)dateNumber){ NSLog(@"js call iOS function j2oFun1/n dataString: %@ |dateNumber :%d",dataString,dateNumber);} RCT_EXPORT_METHOD(j2oFun2:(NSDictionary*)details){ NSString*name=[RCTConvert NSString:details[@"name"]]; NSNumber*age=[RCTConvert NSNumber:details[@"age"]]; NSArray*array=[RCTConvert NSArray:details[@"array"]]; NSLog(@"js call iOS function j2oFun2/n name: %@ | age :%@",name,[agestringValue]); for(inti=0;i<[arraycount];i++){ NSLog(@"array: 第%d個元素:%@",i,array[i]); } } //帶回調函數 RCTResponseSenderBlock ,提供將返回值傳回給js//RCTResponseSenderBlock 只接受一個參數->傳遞給JavaScript回調函數的參數數組RCT_EXPORT_METHOD(j2oCallbackEvent:(NSString*)jsString callback:(RCTResponseSenderBlock)callback){ NSLog(@"js call iOS function: j2oCallbackEvent /n jsString:%@",jsString); NSArray*events=[[NSArrayalloc] initWithObjects:@"Himi",@"12321",nil]; callback(@[[NSNullnull],events]);} //====================================[OC -> JS]=======================================@synthesizebridge=_bridge; //此函數是為了測試OC->JS過程,觸發事件的函數RCT_EXPORT_METHOD(emitterO2J){ [selfocCallJsFun];} -(void)ocCallJsFun{ NSDictionary*direct=@{@"name":@"Himi",@"age":@12}; [self.bridge.eventDispatcher sendAppEventWithName:@"eventName" body:direct]; // [self.bridge.eventDispatcher sendAppEventWithName:@"eventName" body:@{@"name":@"Himi",@"age": @12}]; } @end | 
Main.js:
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 | importReact,{Component}from'react';import{ View, Text, StyleSheet, Image, Alert, NativeAppEventEmitter,//引用NativeAppEventEmitter組件進行監聽Native端派發的事件}from'react-native'; varTestOJO=require('react-native').NativeModules.TestOJO; varo2cFun=NativeAppEventEmitter.addListener( 'eventName', (para)=>Alert.alert('被OC觸發','字典數據:/n name:'+para.name+'/n age:'+para.age));// 千萬不要忘記忘記取消訂閱, 通常在componentWillUnmount函數中實現。// o2cFun.remove(); exportdefaultclassMainextendsComponent{constructor(props){super(props);this.state={ selectedTab:'home' };} componentWillUnmount(){ o2cFun.remove(); } render(){ return( <Viewstyle={{flex:1,alignItems:'center'}}> <Textstyle={styles.himiTextStyle}>HimiReactNative系列教程</Text> <Text onPress={()=>{ TestOJO.j2oFun1('Himi',12321); TestOJO.j2oFun2({ name:'Himi', age:12, array:[ 'hi,Himi','i,m','a array!' ] }); TestOJO.j2oCallbackEvent('Himi',(error,callBackEvents)=>{ if(error){ console.error(error); }else{ Alert.alert('J2O帶返回值','數組的三個值:/n[0]:'+callBackEvents[0]+'/n[1]:'+callBackEvents[1]+'/n[2]:'+callBackEvents[2]); } }); }} style={styles.himiTextStyle}>JS->OC </Text> <Text onPress={()=>{ TestOJO.emitterO2J(); }} style={styles.himiTextStyle}>JS->OC->JS </Text> </View> ); }}; varstyles=StyleSheet.create({ himiTextStyle:{ backgroundColor:'#eee', color:'#f00', fontSize:30, marginTop:70, },}); | 
下面是運行效果:(點擊看動態圖,主要看演示過程與控制臺輸出哦!)

注意:
1.點擊JS->OC 后,會調用三個函數哦
2.點擊JS->OC->JS, 先是通過JS->OC的臨時函數,觸發OC->JS的過程!
新聞熱點
疑難解答