iOS原生与RN的通信(Swift版)

点击上方“iOS开发”,选择“置顶公众号”

关键时刻,第一时间送达!


因为实习的原因,已经好久没有写博客了。倒不是因为忙,而是因为每天都被业务代码填满,找不到很好的可以拿来写博客的素材。


我的公司是一家小公司,我一个人做Android开发,然后某天我技术主管让我学学React Native,顺带把iOS也接过去。


于是我踏上了一条不归路......


原来的项目是原生的iOS项目,在完全不懂iOS开发的情况下,靠着官方文档,以及各种教程,iOS接入也算是顺利。但是在原生与RN通信这块地方花了不少时间。因为原本公司的iOS项目是Swift写的,我们技术主管让我需要原生部分实现的地方都用Swift给他写(技术主管就是iOS开发)。


但是官网上的例子都是OC实现的,有关Swift的导出就一小部分介绍而已......网上的博客也是抄来抄去,毫无参考价值。


可能是我不懂iOS开发,也可能是因为我蠢,但为了这些和我一样蠢的人将来能少花点时间,我决定写这篇博客。


跳过各种如何接入啥啥的步骤,我们直接从iOS原生与RN如何通信开始(介绍不会全面,只介绍目前我项目中用的几种通信):


RN调用iOS原生方法


这部分官网是有教程的,也包括如何用Swift实现,步骤如下:


1:新建一个IOSIntentModule.h文件:


#import <React/RCTBridgeModule.h>

#import <React/RCTLog.h>


@interface IOSIntentModule : NSObject <RCTBridgeModule>

@end


2:新建一个IOSIntentModule.m文件:


#import "IOSIntentModule.h"


@implementation IOSIntentModule.h


RCT_EXPORT_MODULE();


RCT_EXPORT_METHOD(test:(NSString *)name)

{

  NSLog(name);

}


@end


3:在RN中调用:


import { NativeModules } from 'react-native';

NativeModules. IOSIntentModule.test('Hello');


当你执行到“NativeModules. IOSIntentModule.test('Hello');” 这句的时候,就会去调用原生的test方法。然后会在控制台打印”Hello“。


“NativeModules”是RN里和原生通信的一个组件。“IOSIntentModule”是原生导出给RN调用的组件的名字,这个名字是在步骤2中,由“RCT_EXPORT_MODULE();”这个宏指定的。


比如你这样写:“RCT_EXPORT_MODULE(iAmSoHandsome);” 。


那你就得这么调用:


“NativeModules. iAmSoHandsome.test('Hello');”。


如果你不写,那这个名字就和你的类名一样,在本例中就是“ IOSIntentModule”。


“RCT_EXPORT_METHOD()”这个宏是用来将原生的方法导出,只有用这个宏包裹的方法,才可以被RN调用。


那这样的话,其实是RN调用原生的OC方法了,如果我们想用Swift来写怎么办呢?


官网介绍,Swift不支持宏。所以我们还是得靠OC来当作一个桥梁,也就是RN——>OC——>Swift。RN调用原生的OC方法,然后OC在交给Swift去处理。具体步骤如下:


1:新建一个IOSIntentModule.swift文件:


@objc(IOSIntentModule)

class IOSIntentModule: NSObject {


  @objc func test(name: String) -> Void {

    print(name)

  }


}


2:修改一下我们的IOSIntentModule.m文件:


#import <React/RCTBridgeModule.h>


@interface RCT_EXTERN_MODULE(IOSIntentModule, NSObject)


RCT_EXTERN_METHOD(test:(NSString *)name)


@end


3:在桥接文件中引入RCTBridgeModule.h:


一旦你在项目混用OC和Swift两种语言,那就需要这个桥接文件。一般在项目的Supporting Files文件夹里(一般系统会自动生成的,叫做“项目名-Bridging-Header.h”,项目名就是你工程的项目名)。


在里面加上:


#import <React/RCTBridgeModule.h>


然后你就可以在RN中通过“NativeModules. IOSIntentModule.test('Hello');”调用了。虽然看不懂iOS代码,但是我猜测应该是RN先调用OC的方法,然后OC再去调用Swift的方法。


这样你照猫画虎的多写几个方法,然后就可以愉快的用Swift调用啦。


RN调用iOS原生方法并回调


有时候我们并不想简单的调用方法,让他执行完毕就OK了这么简单。我们需要接收他们的返回值啥的,这个时候我们就需要一个回调。


比如进行图片选择的时候,需要RN调用原生的图片选择器,然后将图片地址返回。


回调的方式有两种,一种是callback,一种是Promise,个人比较喜欢Promise,所以下面以Promise为例子(callback方法其实一样的,可以看看官网例子):


1:我们修改最开始的IOSIntentModule.m文件:


#import "IOSIntentModule.h"


@implementation IOSIntentModule.h


RCT_EXPORT_MODULE();


RCT_EXPORT_METHOD(test:(NSString *)name

                  resolver:(RCTPromiseResolveBlock)resolve

                  rejecter:(RCTPromiseRejectBlock)reject)

{

  resolve(name);

}


@end


在这里我们增加了两个参数。只要在方法参数的最后面(注意是最后面)加上

resolver:(RCTPromiseResolveBlock)resolve

rejecter:(RCTPromiseRejectBlock)reject

这两个参数,就会产生一个Promise对象。


然后你就可以用resolve("信息")来返回正常信息。用reject("错误信息")来返回错误信息。


2:然后你就可以在RN中这样来接收这个返回值:


NativeModules.IOSIntentModule.test().then(msg => {

                                   //msg就是你用resolve返回的值

                                }).catch(error => {

                                    //error就是你用reject返回的值

                                });


但是这样的话,是用OC写的,我们如何用Swift来完成呢?


很简单,你在OC的test方法里,调用Swift的方法,然后接收Swift方法的返回值,再将其返回就可以了。那这里就涉及一个问题,如何在OC里调用Swift的方法呢?


OC调用Swift方法


这部分内容其实百度就可以知道了,具体步骤如下:


1:进入你项目的Build Settings里,将Defines Module设置为YES。


设置 Product Module Name ,也可以不设置,默认为工程的名字。


2:编写一个Swift类,就叫做IOSIntentModuleSwift.swift好了:


public class IOSIntentModuleSwift :NSObject{


    public func testSwift(_ name:String) -> String {

        return name

    }

 

}


注意这个类一定要继承NSObject,不然OC会找不到这个类。


还有就是参数最前面要加上“ _ ”,不然会报参数不匹配的错误(百度了一下说是swift3 的一个语法,参数必须有名字,但是从RN传过来的参数没有名字啥啥的,不懂iOS开发......总之加上“ _ ”就行了)。


3:在IOSIntentModule.m中添加头文件:“项目名-Swift.h” 。这个项目名就是之前让你设置的Product Module Name ,没设置的话就是你的工程名。


4:然后你就可以在OC中调用Swift了,就像这样子:


#import "IOSIntentModule.h"

#import "项目名-Swift.h"


@implementation IOSIntentModule.h


RCT_EXPORT_MODULE();


RCT_EXPORT_METHOD(test:(NSString *)name

                  resolver:(RCTPromiseResolveBlock)resolve

                  rejecter:(RCTPromiseRejectBlock)reject)

{

    IOSIntentModuleSwift* intentSwift = [[IOSIntentModuleSwift alloc] init];

    NSString* str = [intentSwift testSwift:name];

    resolve(str);

}


@end


这样当调用到OC的test方法时,就会去执行Swift的testSwift方法,然后testSwift将其接收到的参数“name”返回,然后OC接收到Swift方法的返回值,再将其返回给RN。


正常来说这样就行了,但是现实总会有奇奇怪怪的事情发生。比如我公司的项目......加了“项目名-Swift.h”这个头文件后,总是编译不过去,说“项目名-Swift.h”里的一个@import xxxxx找不到。


解决办法也很简单。其实“项目名-Swift.h”就是一个桥接文件,里面的内容大概长这样:


SWIFT_CLASS("_TtC6alltuu20IOSIntentModuleSwift")

@interface IOSIntentModuleSwift : NSObject

- (NSDictionary * _Nonnull)pickImage SWIFT_WARN_UNUSED_RESULT;

- (NSString * _Nonnull)reactUplaodPicToOSS:(NSString * _Nonnull)bucket albumSetId:(NSString * _Nonnull)albumSetId objectkey:(NSString * _Nonnull)objectkey filePath:(NSString * _Nonnull)filePath contentId:(NSInteger)contentId type:(NSInteger)type SWIFT_WARN_UNUSED_RESULT;

- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;

@end


你可以自己新建一个比如说“IOSIntentModule-Swift.h”文件,然后把“项目名-Swift.h”文件的东西拿出来,去掉报错的地方,粘贴进去。然后在”IOSIntentModule.m“里直接“#import IOSIntentModule-Swift.h”就可以了。


iOS原生主动给RN发送事件


这一部分我卡了好久......后来我仔仔细细的看了下官网,发现有一句不起眼的话......


官网介绍.png


大家看最后一句,通过bridge向JS发送事件。


然后我尝试了一下,发现RCTRootView里就有一个bridge,这个bridge里有个eventDispatcher(),然后这个eventDispatcher()里就有三个发送事件的方法(虽然说是快过时了,但是可以用,而且用起来简单)


代码类似于这样子:


(ctrl.view as! RCTRootView).bridge.eventDispatcher().sendDeviceEvent(withName: "refresh", body: "Hello")


这边我们用的是sendDeviceEvent()方法,还有另外两种,不知道啥区别......大家可以自行百度。


其中withName:"refresh"表示你要发送的事件的名字,在RN中监听的时候,也要监听这个名字。body:"Hello"就是你要发送的数据。


然后你就可以在RN中监听这个事件:


    //组件渲染之前调用此方法

    componentWillMount() {

        this.subscription = DeviceEventEmitter.addListener('refresh', function(msg) {

            alert(msg);

        });

    }


    componentWillUnmount() {

        // 移除监听器

        this.subscription.remove();

    }


我们监听了refresh事件,当iOS原生那边发送的时候,就会被RN接收到。msg就是iOS原生那边的body参数。


结束


虽然没有全面将iOS原生与RN通信的方式全部写完。但是我觉得有这三个例子,看看官网,举一反三应该没啥问题。而且我们的重点是如何用Swift来实现通信的逻辑。


还有就是有问题多看看官网...逐字逐句的那种,官网超坑的(也可能是我太垃圾)。


那就这样结束了,才疏学浅,有不对的地方,还请大家批评指正。


最后


感谢我可爱的女朋友。



  • 来自:陈添

  • 链接:http://www.jianshu.com/p/956802d37158

  • iOS开发整理发布,转载请联系作者授权

【点击成为Android大神】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值