logo

【译】React Native与ios交互

开发干货滴答答2018-10-22

[译]React Native与ios交互

原生和RN混合开发中的交互

原生加载RN

  1. RN 端 创建好React Native 项目后,项目中创建了 index.ios.js 的js文件,这是RN中iOS的js端入口文件,我们可以在里边添加代码如下:

    import React, { Component } from 'react';
    import { AppRegistry, View, Text} from 'react-native';
    class HelloWorldCp extends Component {
        render() {
           return (
            
              Hello world!
            
         );
      }
    }
    AppRegistry.registerComponent('HelloWorldCp', () => HelloWorldCp);

在这里,我们创建了一个 HelloWorldCp 的React组件,并用 AppRegistry.registerComponent注册了该组件,这样原生系统才可以使用该组件。在组件里,我们在默认的 render() 方法中输出了默认的view,view下包含了一个 “Hello World!” 的标签。对于view,设置了一个style,大意是将view下的 标签置于View的中央。

  1. ios 端 接下来的事情便是在原生UI中将这个组件显示出来,我们需要用到React容器类RCTRootView。加载代码如下:

    NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
    RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                    moduleName:@"HelloWorldCp"
                                             initialProperties:nil
                                                 launchOptions:nil];
    UIViewController *vc = [[UIViewController alloc] init];
    vc.view = rootView;
    [self.navigationController pushViewController:vc animated:YES];

看代码可知,首先我们初始化了一个 NSURL 对象,它指向本地 JS 的调试服务地址,以供 RCTRootView 初始化时使用。RCTRootView 用来承载 JS 特定的组件,在原生下可 以当做普通的 UIView 来进行处理,如添加到 superview,设置frame等操作。初始化时第一个参数为 JS 文件的服务器地址,moduleName 是 React 中注册好的组件, initialProperties 接收一个字典,用来 传递参数给 JS,最后一个则是启动项参数。在这里,我们加载了上面创建的 HelloWorldCp 组件。最后将初始化的 RCTRootView 设 置成新页面的根view并展示。

  1. 初始化RCTRootView数据传递
      NSDictionary *param = @{@"scores" :@[
                                     @{@"name" : @"Alex",@"value": @"42"},
                                     @{@"name" : @"Joel",@"value": @"10"},
                                     @{@"name" : @"Zona",@"value": @"20"}
                                    ]
                        };

      NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios"];
      RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                    moduleName:@"ParamPassCp"
                                             initialProperties:param
                                                 launchOptions:nil];

      UIViewController *vc = [[UIViewController alloc] init];
      vc.view = rootView;
      [self.navigationController pushViewController:vc animated:YES];

相比于上面 Hello World的例子,这里初始化了一个字典,存储了一些名字及对应的分数,并在 RCTRootView 初始化的时候作为 initialProperties 的参数进行传递。 RN 接收参数代码如下:

        class ParamPassCp extends React.Component {
         render() {
             var contents = this.props["scores"].map(
             score => {score.name}:{score.value}{"\n"}
          );
            return (
             
                
                    {contents}
                
             
            );
           }
        }

RN调用原生

要想让iOS类内的方法能够被RN调用,类比RN端的组件注册,iOS端同样需要注册该类。首先便需要原生类实现协议:RCTBridgeModule,实现该协议的类,会自动注册到Object-C对应的Bridge中。所以定义可以让RN调用的类可以这样写: ​

         #import "RCTBridgeModule.h"
         @interface RNIOSLog : NSObject
         @end

所有实现 RCTBridgeModule 的类都必须显示的使用宏命令:

         @implementation RNIOSLog
         RCT_EXPORT_MODULE();
         @end

该宏的作用是:自动为该类注册为JS端的模块,当Object-c Bridge加载的时候。这个类注册的模块可以被JavaScript Bridge调用。当然该宏可以接受一个参数作为注册的模块名,默认值是该类的名称。注册完模块之后,还需要注册模块下需要暴露给JS的方法。此外,暴露出的方法返回值必须为void。 ​

        RCT_EXPORT_METHOD(show:(NSString *)msg){
          NSLog(@"msg:%@",msg);
        }

原生的模块方法注册好之后,JS端代码如下:

      import {NativeModules} from "react-native";
      var RNIOSLog = NativeModules.RNIOSLog;

      class RNLogCp extends Component {
           render() {
              return (
                  
            
                      RNIOSLog.show('from react native')}
                                    style={styles.btn}>
                        showLog
                        
                  
                
                  
                );
            }
        }

原生调用RN

该类需要继承自 RCTEventEmitter,如下:

       #import "RCTEventEmitter.h"  
       @interface CallRNTest : RCTEventEmitter
       @end

然后在 .m 文件中,在子类中为父类 RCTEventEmitter 的 bridge 生成 set/get方法,并使用用于导出模块的宏。

    @implementation CallRNTest
      @synthesize bridge = _bridge;
       RCT_EXPORT_MODULE();
    @end
    -(NSArray *)supportedEvents{
        return @[@"callRn"];
     }
     -(void)nativeCallRn:(NSString*)code result:(NSString*) result
         {
            [self sendEventWithName:@"callRn"
                 body:@{
                        @"code": code,
                        @"result": result,
                        }];
      }

js端代码

    import { ...  NativeModules,  NativeEventEmitter} from 'react-native';  
    var CallRNTest = NativeModules.CallRNTest;
    const myNativeEvt = new NativeEventEmitter(CallRNTest);
    componentWillMount() {
        this.listener = myNativeEvt.addListener('callRn', this.callRn.bind(this));  
    }
     componentWillUnmount() {
     this.listener && this.listener.remove(); 
     this.listener = null;
    }
    callRn(data) {
      console.warn(data.code, data.result);
    }

本文已由作者授权发布,版权属于创宇前端。欢迎注明出处转载本文。本文链接:https://knownsec-fed.com/2018-10-22-yi-reactnative-yu-ios-jiao-hu/
想要看到更多来自知道创宇开发一线的分享,请搜索关注我们的微信公众号:创宇前端(KnownsecFED)。欢迎留言讨论,我们会尽可能回复。
感谢您的阅读。