React-native調(diào)用iOS原生模塊

基本調(diào)用方法

1.創(chuàng)建原生代碼,實(shí)現(xiàn)代理

  • 在CalendarManager.h文件中
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>

NS_ASSUME_NONNULL_BEGIN

@interface CalendarManager : NSObject<RCTBridgeModule>

@end

NS_ASSUME_NONNULL_END
  • 在CalendarManager.m中
#import "CalendarManager.h"

@implementation CalendarManager


/*
 RCT_EXPORT_MODULE()宏。這個(gè)宏也可以添加一個(gè)參數(shù)用來(lái)指定在 JavaScript中訪問(wèn)這個(gè)模塊的名字峦萎。如果你不指定糟袁,默認(rèn)就會(huì)使用這個(gè)Objective-C 類的名字掸哑。如果類名以 RCT 開(kāi)頭蒸辆,則 JavaScript 端引入的模塊名會(huì)自動(dòng)移除這個(gè)前綴跟磨。
 
 */
RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
  
  NSLog(@"Pretending to create an event %@ at %@", name, location);
  RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
  
  
}

@end
  • 在React-native調(diào)用
import React, {
    Component
} from 'react';
import {
    NativeModules,
    Button,
    Alert,
    SafeAreaView
} from 'react-native';

const CalendarManager = NativeModules.CalendarManager;

export default class IScrolledDown extends Component {

    onclickAction() {

        Alert.alert('調(diào)用原生');
        CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');
    }

    render() {
        return (
          <SafeAreaView>
           <Button onPress={this.onclickAction} title="點(diǎn)我"></Button>
          </SafeAreaView>
        );
    }
}

支持的類型

RCT_EXPORT_METHOD 支持所有標(biāo)準(zhǔn) JSON 類型定罢,包括:

  • string (NSString)
  • number (NSInteger, float, double, CGFloat, NSNumber)
  • boolean (BOOL, NSNumber)
  • array (NSArray) 可包含本列表中任意類型
  • object (NSDictionary) 可包含 string 類型的鍵和本列表中任意類型的值
  • function (RCTResponseSenderBlock)

類型轉(zhuǎn)換

  • 在iOS中通過(guò)RCTConvert類轉(zhuǎn)換
#import <React/RCTConvert.h>

RCT_EXPORT_METHOD(addEvent:(NSString *)name details:(NSDictionary *)details)
{
  NSString *location = [RCTConvert NSString:details[@"location"]];
  NSDate *time = [RCTConvert NSDate:details[@"time"]];
  ...
}
  • 在JS中
CalendarManager.addEvent('Birthday Party', {
  location: '4 Privet Drive, Surrey',
  time: date.getTime(),
  description: '...',
});

帶有回調(diào)函數(shù)

  • 在iOS原生中
RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback)
{
  NSArray *events = ...
  callback(@[[NSNull null], events]);
}

RCTResponseSenderBlock只接受一個(gè)參數(shù)——傳遞給 JavaScript 回調(diào)函數(shù)的參數(shù)數(shù)組糠溜。在上面這個(gè)例子里我們用 Node.js 的常用習(xí)慣:第一個(gè)參數(shù)是一個(gè)錯(cuò)誤對(duì)象(沒(méi)有發(fā)生錯(cuò)誤的時(shí)候?yàn)?null)淳玩,而剩下的部分是函數(shù)的返回值。

  • 在js中
CalendarManager.findEvents((error, events) => {
  if (error) {
    console.error(error);
  } else {
    this.setState({events: events});
  }
});

原生模塊通常只應(yīng)調(diào)用回調(diào)函數(shù)一次非竿。但是蜕着,它可以保存 callback 并在將來(lái)調(diào)用。這在封裝那些通過(guò)“委托函數(shù)”來(lái)獲得返回值的 iOS API 時(shí)最為常見(jiàn)红柱。RCTAlertManager中就屬于這種情況

Promises

  • 在原生中
RCT_REMAP_METHOD(findEvents,
                 findEventsWithResolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
  NSArray *events = ...
  if (events) {
    resolve(events);
  } else {
    NSError *error = ...
    reject(@"no_events", @"There were no events", error);
  }
}
  • 在js中
async function updateEvents() {
  try {
    const events = await CalendarManager.findEvents();

    this.setState({events});
  } catch (e) {
    console.error(e);
  }
}

updateEvents();

現(xiàn)在 JavaScript 端的方法會(huì)返回一個(gè) Promise承匣。這樣你就可以在一個(gè)聲明了async的異步函數(shù)內(nèi)使用await關(guān)鍵字來(lái)調(diào)用,并等待其結(jié)果返回锤悄。(雖然這樣寫(xiě)著看起來(lái)像同步操作韧骗,但實(shí)際仍然是異步的,并不會(huì)阻塞執(zhí)行來(lái)等待)

多線程

  • CalendarManager所有方法都在主線程
- (dispatch_queue_t)methodQueue
{
  return dispatch_get_main_queue();
}
  • CalendarManager所有方法在另一個(gè)線程
- (dispatch_queue_t)methodQueue
{
  return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
}
  • CalendarManager中只有一個(gè)方法在多線程
RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback)
{
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 在這里執(zhí)行長(zhǎng)時(shí)間的操作
    ...
    // 你可以在任何線程/隊(duì)列中執(zhí)行回調(diào)函數(shù)
    callback(@[...]);
  });
}

methodQueue方法會(huì)在模塊被初始化的時(shí)候被執(zhí)行一次零聚,然后會(huì)被 React Native 的橋接機(jī)制保存下來(lái)袍暴,所以你不需要自己保存隊(duì)列的引用,除非你希望在模塊的其它地方使用它

導(dǎo)出常量

  • 原生
- (NSDictionary *)constantsToExport
{
  return @{ @"firstDayOfTheWeek": @"Monday" };
}
  • js中
console.log(CalendarManager.firstDayOfTheWeek);

但是注意這個(gè)常量?jī)H僅在初始化的時(shí)候?qū)С隽艘淮瘟ブⅲ约词鼓阍谶\(yùn)行期間改變constantToExport返回的值容诬,也不會(huì)影響到 JavaScript 環(huán)境下所得到的結(jié)果。

給 JavaScript 端發(fā)送事件

即使沒(méi)有被 JavaScript 調(diào)用沿腰,原生模塊也可以給 JavaScript 發(fā)送事件通知。最好的方法是繼承RCTEventEmitter狈定,實(shí)現(xiàn)suppportEvents方法并調(diào)用self sendEventWithName:颂龙。

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface CalendarManager : RCTEventEmitter <RCTBridgeModule>

@end
// CalendarManager.m
#import "CalendarManager.h"

@implementation CalendarManager

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
  return @[@"EventReminder"];
}

- (void)calendarEventReminderReceived:(NSNotification *)notification
{
  NSString *eventName = notification.userInfo[@"name"];
  [self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];
}

@end

JavaScript 端的代碼可以創(chuàng)建一個(gè)包含你的模塊的NativeEventEmitter實(shí)例來(lái)訂閱這些事件

import { NativeEventEmitter, NativeModules } from 'react-native';
const { CalendarManager } = NativeModules;

const calendarManagerEmitter = new NativeEventEmitter(CalendarManager);

const subscription = calendarManagerEmitter.addListener(
  'EventReminder',
  (reminder) => console.log(reminder.name)
);
...
// 別忘了取消訂閱习蓬,通常在componentWillUnmount生命周期方法中實(shí)現(xiàn)。
subscription.remove();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末措嵌,一起剝皮案震驚了整個(gè)濱河市躲叼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌企巢,老刑警劉巖枫慷,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異浪规,居然都是意外死亡或听,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)笋婿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)誉裆,“玉大人,你說(shuō)我怎么就攤上這事缸濒∽愣” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵庇配,是天一觀的道長(zhǎng)斩跌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)捞慌,這世上最難降的妖魔是什么耀鸦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮卿闹,結(jié)果婚禮上揭糕,老公的妹妹穿的比我還像新娘。我一直安慰自己锻霎,他們只是感情好著角,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著旋恼,像睡著了一般吏口。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上冰更,一...
    開(kāi)封第一講書(shū)人閱讀 52,337評(píng)論 1 310
  • 那天产徊,我揣著相機(jī)與錄音,去河邊找鬼蜀细。 笑死舟铜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的奠衔。 我是一名探鬼主播谆刨,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼塘娶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了痊夭?” 一聲冷哼從身側(cè)響起刁岸,我...
    開(kāi)封第一講書(shū)人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎她我,沒(méi)想到半個(gè)月后虹曙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡番舆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年酝碳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片合蔽。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡击敌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拴事,到底是詐尸還是另有隱情沃斤,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布刃宵,位于F島的核電站衡瓶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏牲证。R本人自食惡果不足惜哮针,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坦袍。 院中可真熱鬧十厢,春花似錦、人聲如沸捂齐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)奠宜。三九已至包颁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間压真,已是汗流浹背娩嚼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留滴肿,地道東北人岳悟。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像泼差,于是被迫代替她去往敵國(guó)和親竿音。 傳聞我的和親對(duì)象是個(gè)殘疾皇子和屎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容