前言
最近用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"];
- 創建的iOS交互類要引用
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_METHOD
與RCT_REMAP_METHOD
宏定義的使用區別(個人總結,有不對請指正)-
RCT_EXPORT_METHOD
:用于僅有一個參數或回調 -
RCT_REMAP_METHOD
:用于有多個參數或(和)多個回調
(了解更多可以看RN宏定義源碼1
,下面貼出關鍵兩句)
-
- 注意點2:iOS回調方式有兩種
callback(@[jsonString]); ((RCTPromiseResolveBlock)resolver)
- Promise方式:
_resolveBlock(@[jsonString]); ((RCTResponseSenderBlock)callback)
(了解更多看RN源碼2
)
- 注意點1:
源碼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
如上有問題歡迎各路大神留言指正,寫博客不容易,大家相互學習。