跳到主要內容

iOS 原生模組

資訊

原生模組和原生組件是舊版架構使用的穩定技術。當新架構穩定後,它們將在未來被棄用。新架構使用 Turbo 原生模組Fabric 原生組件 來達到類似的結果。

歡迎來到 iOS 原生模組。請先閱讀 原生模組簡介,以了解原生模組是什麼。

建立日曆原生模組

在接下來的指南中,您將建立一個名為 CalendarModule 的原生模組,讓您可以從 JavaScript 存取 Apple 的日曆 API。到最後,您將能夠從 JavaScript 呼叫 CalendarModule.createCalendarEvent('Dinner Party', 'My House');,調用一個建立日曆事件的原生方法。

設定

首先,在 Xcode 中開啟 React Native 應用程式中的 iOS 專案。您可以在 React Native 應用程式中的這裡找到您的 iOS 專案

Image of opening up an iOS project within a React Native app inside of xCode.
您可以在哪裡找到 iOS 專案的圖片

我們建議使用 Xcode 來編寫您的原生程式碼。Xcode 是為 iOS 開發而建置的,使用它可以幫助您快速解決較小的錯誤,例如程式碼語法。

建立自訂原生模組檔案

第一步是建立我們主要的自訂原生模組標頭和實作檔案。建立一個名為 RCTCalendarModule.h 的新檔案

Image of creating a class called  RCTCalendarModule.h.
在與 AppDelegate 相同的資料夾中建立自訂原生模組檔案的圖片

並將以下內容添加到其中

objectivec
//  RCTCalendarModule.h
#import <React/RCTBridgeModule.h>
@interface RCTCalendarModule : NSObject <RCTBridgeModule>
@end

您可以使用任何適合您正在建置的原生模組的名稱。將類別命名為 RCTCalendarModule,因為您正在建立日曆原生模組。由於 ObjC 沒有像 Java 或 C++ 那樣的語言層級命名空間支援,因此慣例是在類別名稱前加上一個子字串。這可以是您的應用程式名稱或基礎架構名稱的縮寫。在此範例中,RCT 指的是 React。

如下所示,CalendarModule 類別實作了 RCTBridgeModule 協定。原生模組是一個實作 RCTBridgeModule 協定的 Objective-C 類別。

接下來,讓我們開始實作原生模組。使用 Xcode 中的 Cocoa Touch 類別建立對應的實作檔案 RCTCalendarModule.m,在相同的資料夾中,並包含以下內容

objectivec
// RCTCalendarModule.m
#import "RCTCalendarModule.h"

@implementation RCTCalendarModule

// To export a module named RCTCalendarModule
RCT_EXPORT_MODULE();

@end

模組名稱

目前,您的 RCTCalendarModule.m 原生模組僅包含一個 RCT_EXPORT_MODULE 巨集,它匯出並向 React Native 註冊原生模組類別。RCT_EXPORT_MODULE 巨集還接受一個可選參數,用於指定模組在您的 JavaScript 程式碼中可存取的名稱。

此參數不是字串字面值。在下面的範例中,傳遞的是 RCT_EXPORT_MODULE(CalendarModuleFoo),而不是 RCT_EXPORT_MODULE("CalendarModuleFoo")

objectivec
// To export a module named CalendarModuleFoo
RCT_EXPORT_MODULE(CalendarModuleFoo);

然後可以像這樣在 JS 中存取原生模組

tsx
const {CalendarModuleFoo} = ReactNative.NativeModules;

如果您未指定名稱,JavaScript 模組名稱將與 Objective-C 類別名稱相符,並移除任何 "RCT" 或 "RK" 前綴。

讓我們遵循以下範例,並在不帶任何參數的情況下呼叫 RCT_EXPORT_MODULE。因此,模組將使用名稱 CalendarModule 公開給 React Native,因為那是 Objective-C 類別名稱,並移除了 RCT。

objectivec
// Without passing in a name this will export the native module name as the Objective-C class name with “RCT” removed
RCT_EXPORT_MODULE();

然後可以像這樣在 JS 中存取原生模組

tsx
const {CalendarModule} = ReactNative.NativeModules;

匯出原生方法到 JavaScript

除非明確告知,否則 React Native 不會將原生模組中的任何方法公開給 JavaScript。這可以使用 RCT_EXPORT_METHOD 巨集來完成。在 RCT_EXPORT_METHOD 巨集中撰寫的方法是異步的,因此傳回類型始終為 void。為了將結果從 RCT_EXPORT_METHOD 方法傳遞到 JavaScript,您可以使用回呼或發射事件(在下面涵蓋)。讓我們繼續為我們的 CalendarModule 原生模組使用 RCT_EXPORT_METHOD 巨集設定一個原生方法。將其命名為 createCalendarEvent(),目前讓它接受名稱和位置參數作為字串。參數類型選項將在稍後介紹。

objectivec
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
}

請注意,除非您的方法依賴 RCT 參數轉換(請參閱下面的參數類型),否則使用 TurboModules 將不需要 RCT_EXPORT_METHOD 巨集。最終,React Native 將移除 RCT_EXPORT_MACRO,因此我們不鼓勵人們使用 RCTConvert。相反,您可以在方法主體中進行參數轉換。

在您建立 createCalendarEvent() 方法的功能之前,請在方法中新增一個 console log,以便您可以確認它已從您的 React Native 應用程式中的 JavaScript 調用。使用 React 的 RCTLog API。讓我們在檔案頂部匯入該標頭,然後新增 log 呼叫。

objectivec
#import <React/RCTLog.h>
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}

同步方法

您可以使用 RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD 來建立同步原生方法。

objectivec
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getName)
{
return [[UIDevice currentDevice] name];
}

此方法的傳回類型必須為物件類型 (id),並且應該可序列化為 JSON。這表示 hook 只能傳回 nil 或 JSON 值(例如 NSNumber、NSString、NSArray、NSDictionary)。

目前,我們不建議使用同步方法,因為同步呼叫方法可能會對效能造成嚴重影響,並將與執行緒相關的錯誤引入您的原生模組。此外,請注意,如果您選擇使用 RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD,您的應用程式將無法再使用 Google Chrome 除錯工具。這是因為同步方法需要 JS VM 與應用程式共用記憶體。對於 Google Chrome 除錯工具,React Native 在 Google Chrome 中的 JS VM 內部執行,並透過 WebSockets 與行動裝置異步通訊。

測試您已建置的內容

此時,您已在 iOS 中為您的原生模組設定了基本骨架。透過存取原生模組並在 JavaScript 中調用其匯出的方法來測試它。

在您的應用程式中找到您想要新增對原生模組 createCalendarEvent() 方法呼叫的位置。以下是一個組件範例 NewModuleButton,您可以將其新增到您的應用程式中。您可以在 NewModuleButtononPress() 函數內部調用原生模組。

tsx
import React from 'react';
import {Button} from 'react-native';

const NewModuleButton = () => {
const onPress = () => {
console.log('We will invoke the native module here!');
};

return (
<Button
title="Click to invoke your native module!"
color="#841584"
onPress={onPress}
/>
);
};

export default NewModuleButton;

為了從 JavaScript 存取您的原生模組,您需要先從 React Native 匯入 NativeModules

tsx
import {NativeModules} from 'react-native';

然後您可以從 NativeModules 存取 CalendarModule 原生模組。

tsx
const {CalendarModule} = NativeModules;

現在您已經可以使用 CalendarModule 原生模組,您可以調用您的原生方法 createCalendarEvent()。下面它被新增到 NewModuleButton 中的 onPress() 方法中

tsx
const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
};

最後一步是重建 React Native 應用程式,以便您可以擁有最新的原生程式碼(以及您的新原生模組!)。在您的命令列中,React Native 應用程式所在的位置,執行以下命令

shell
npm run ios

在迭代時建置

當您逐步完成這些指南並迭代您的原生模組時,您將需要對您的應用程式進行原生重建,才能從 JavaScript 存取您最新的變更。這是因為您正在編寫的程式碼位於應用程式的原生部分中。雖然 React Native 的 metro bundler 可以監看 JavaScript 中的變更並為您即時重建 JS bundle,但它不會對原生程式碼執行此操作。因此,如果您想測試您最新的原生變更,您需要使用上述命令重建。

重點回顧✨

您現在應該能夠在 JavaScript 中調用您的原生模組上的 createCalendarEvent() 方法。由於您在函數中使用 RCTLog,您可以透過在您的應用程式中啟用除錯模式並查看 Chrome 中的 JS 控制台或行動應用程式除錯工具 Flipper,來確認您的原生方法正在被調用。每次您調用原生模組方法時,您都應該看到您的 RCTLogInfo(@"Pretending to create an event %@ at %@", name, location); 訊息。

Image of logs.
Flipper 中 iOS 日誌的圖片

此時,您已建立了一個 iOS 原生模組,並從您的 React Native 應用程式中的 JavaScript 調用了它的一個方法。您可以繼續閱讀以了解更多資訊,例如您的原生模組方法接受哪些參數類型,以及如何在您的原生模組中設定回呼和 Promise。

超越日曆原生模組

更好的原生模組匯出

像上面那樣透過從 NativeModules 中拉出原生模組來匯入它有點笨拙。

為了讓您的原生模組的消費者不必每次想要存取您的原生模組時都這樣做,您可以為模組建立一個 JavaScript 包裝器。建立一個名為 NativeCalendarModule.js 的新 JavaScript 檔案,其中包含以下內容

tsx
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:

* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
export default CalendarModule;

此 JavaScript 檔案也成為您新增任何 JavaScript 端功能的良好位置。例如,如果您使用像 TypeScript 這樣的類型系統,您可以在此處為您的原生模組新增類型註釋。雖然 React Native 尚不支援原生到 JS 類型安全,但透過這些類型註釋,您的所有 JS 程式碼都將是類型安全的。這些註釋也將使您更容易在未來切換到類型安全的原生模組。以下是在日曆模組中新增類型安全性的範例

tsx
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:
*
* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
interface CalendarInterface {
createCalendarEvent(name: string, location: string): void;
}
export default CalendarModule as CalendarInterface;

在您的其他 JavaScript 檔案中,您可以存取原生模組並像這樣調用其方法

tsx
import NativeCalendarModule from './NativeCalendarModule';
NativeCalendarModule.createCalendarEvent('foo', 'bar');

請注意,這假設您匯入 CalendarModule 的位置與 NativeCalendarModule.js 位於相同的階層中。請根據需要更新相對匯入路徑。

參數類型

當在 JavaScript 中調用原生模組方法時,React Native 會將參數從 JS 物件轉換為其 Objective-C/Swift 物件對應物。因此,例如,如果您的 Objective-C 原生模組方法接受 NSNumber,則在 JS 中您需要使用數字呼叫該方法。React Native 將為您處理轉換。以下是原生模組方法支援的參數類型列表,以及它們對應的 JavaScript 等效項。

Objective-CJavaScript
NSStringstring, ?string
BOOLboolean
doublenumber
NSNumber?number
NSArrayArray, ?Array
NSDictionaryObject, ?Object
RCTResponseSenderBlockFunction (成功)
RCTResponseSenderBlock, RCTResponseErrorBlockFunction (失敗)
RCTPromiseResolveBlock, RCTPromiseRejectBlockPromise

目前支援以下類型,但在 TurboModules 中將不支援。請避免使用它們。

  • Function (失敗) -> RCTResponseErrorBlock
  • Number -> NSInteger
  • Number -> CGFloat
  • Number -> float

對於 iOS,您也可以使用 RCTConvert 類別支援的任何參數類型撰寫原生模組方法(請參閱 RCTConvert 以取得有關支援內容的詳細資訊)。RCTConvert 輔助函數都接受 JSON 值作為輸入,並將其對應到原生 Objective-C 類型或類別。

匯出常數

原生模組可以透過覆寫原生方法 constantsToExport() 來匯出常數。下面覆寫了 constantsToExport(),並傳回一個字典,其中包含您可以在 JavaScript 中存取的預設事件名稱屬性,如下所示

objectivec
- (NSDictionary *)constantsToExport
{
return @{ @"DEFAULT_EVENT_NAME": @"New Event" };
}

然後可以透過在 JS 中對原生模組調用 getConstants() 來存取常數,如下所示

tsx
const {DEFAULT_EVENT_NAME} = CalendarModule.getConstants();
console.log(DEFAULT_EVENT_NAME);

從技術上講,可以直接從 NativeModule 物件存取在 constantsToExport() 中匯出的常數。TurboModules 將不再支援此功能,因此我們鼓勵社群切換到上述方法,以避免未來進行必要的遷移。

請注意,常數僅在初始化時匯出,因此如果您在執行時變更 constantsToExport() 值,它將不會影響 JavaScript 環境。

對於 iOS,如果您覆寫 constantsToExport(),那麼您也應該實作 + requiresMainQueueSetup,以讓 React Native 知道您的模組是否需要在主執行緒上初始化,然後才能執行任何 JavaScript 程式碼。否則,您將看到一個警告,指出在未來,除非您明確選擇退出 + requiresMainQueueSetup:,否則您的模組可能會在背景執行緒上初始化。如果您的模組不需要存取 UIKit,則您應該使用 NO 回應 + requiresMainQueueSetup

回呼

原生模組也支援一種獨特的參數類型 - 回呼。回呼用於將資料從 Objective-C 傳遞到 JavaScript 以用於異步方法。它們也可以用於從原生端異步執行 JS。

對於 iOS,回呼是使用類型 RCTResponseSenderBlock 實作的。下面將回呼參數 myCallBack 新增到 createCalendarEventMethod()

objectivec
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title
location:(NSString *)location
myCallback:(RCTResponseSenderBlock)callback)

然後您可以在您的原生函數中調用回呼,在陣列中提供您想要傳遞到 JavaScript 的任何結果。請注意,RCTResponseSenderBlock 僅接受一個參數 - 要傳遞到 JavaScript 回呼的參數陣列。下面您將傳回先前呼叫中建立的事件 ID。

重要的是要強調,回呼不會在原生函數完成後立即調用—請記住通訊是異步的。

objectivec
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)
{
NSInteger eventId = ...
callback(@[@(eventId)]);

RCTLogInfo(@"Pretending to create an event %@ at %@", title, location);
}

然後可以使用以下 JavaScript 存取此方法

tsx
const onSubmit = () => {
CalendarModule.createCalendarEvent(
'Party',
'04-12-2020',
eventId => {
console.log(`Created a new event with id ${eventId}`);
},
);
};

原生模組應該只調用其回呼一次。但是,它可以儲存回呼並稍後調用它。此模式通常用於包裝需要委派的 iOS API—請參閱 RCTAlertManager 作為範例。如果從未調用回呼,則會洩漏一些記憶體。

使用回呼進行錯誤處理有兩種方法。第一種方法是遵循 Node 的慣例,並將傳遞到回呼陣列的第一個參數視為錯誤物件。

objectivec
RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)
{
NSNumber *eventId = [NSNumber numberWithInt:123];
callback(@[[NSNull null], eventId]);
}

在 JavaScript 中,您可以檢查第一個參數,以查看是否已傳遞錯誤

tsx
const onPress = () => {
CalendarModule.createCalendarEventCallback(
'testName',
'testLocation',
(error, eventId) => {
if (error) {
console.error(`Error found! ${error}`);
}
console.log(`event id ${eventId} returned`);
},
);
};

另一種選擇是使用兩個獨立的回呼:onFailure 和 onSuccess。

objectivec
RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title
location:(NSString *)location
errorCallback: (RCTResponseSenderBlock)errorCallback
successCallback: (RCTResponseSenderBlock)successCallback)
{
@try {
NSNumber *eventId = [NSNumber numberWithInt:123];
successCallback(@[eventId]);
}

@catch ( NSException *e ) {
errorCallback(@[e]);
}
}

然後在 JavaScript 中,您可以為錯誤和成功回應新增一個獨立的回呼

tsx
const onPress = () => {
CalendarModule.createCalendarEventCallback(
'testName',
'testLocation',
error => {
console.error(`Error found! ${error}`);
},
eventId => {
console.log(`event id ${eventId} returned`);
},
);
};

如果您想要將類似錯誤的物件傳遞到 JavaScript,請使用 RCTUtils.h. 中的 RCTMakeError。目前,這僅將一個錯誤形狀的字典傳遞到 JavaScript,但 React Native 的目標是在未來自動產生真實的 JavaScript Error 物件。您也可以提供 RCTResponseErrorBlock 參數,該參數用於錯誤回呼並接受 NSError \* 物件。請注意,TurboModules 將不支援此參數類型。

Promise

原生模組也可以實現 Promise,這可以簡化您的 JavaScript,尤其是在使用 ES2016 的 async/await 語法時。當原生模組方法的最後一個參數是 RCTPromiseResolveBlockRCTPromiseRejectBlock 時,其對應的 JS 方法將傳回 JS Promise 物件。

將上述程式碼重構為使用 Promise 而不是回呼,如下所示

objectivec
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title
location:(NSString *)location
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSInteger eventId = createCalendarEvent();
if (eventId) {
resolve(@(eventId));
} else {
reject(@"event_failure", @"no event id returned", nil);
}
}

此方法的 JavaScript 對應物會傳回 Promise。這表示您可以在 async 函數中使用 await 關鍵字來呼叫它並等待其結果

tsx
const onSubmit = async () => {
try {
const eventId = await CalendarModule.createCalendarEvent(
'Party',
'my house',
);
console.log(`Created a new event with id ${eventId}`);
} catch (e) {
console.error(e);
}
};

傳送事件到 JavaScript

原生模組可以向 JavaScript 發出事件訊號,而無需直接調用。例如,您可能想要向 JavaScript 發出提醒訊號,指出原生 iOS 日曆應用程式中的日曆事件即將發生。執行此操作的首選方法是子類化 RCTEventEmitter,實作 supportedEvents 並呼叫 self sendEventWithName

更新您的標頭類別以匯入 RCTEventEmitter 並子類化 RCTEventEmitter

objectivec
//  CalendarModule.h

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

@interface CalendarModule : RCTEventEmitter <RCTBridgeModule>
@end

JavaScript 程式碼可以透過圍繞您的模組建立新的 NativeEventEmitter 實例來訂閱這些事件。

如果您在沒有監聽器的情況下不必要地發射事件而浪費資源,您將收到警告。為了避免這種情況並最佳化您模組的工作負載(例如,透過取消訂閱上游通知或暫停背景任務),您可以在您的 RCTEventEmitter 子類別中覆寫 startObservingstopObserving

objectivec
@implementation CalendarModule
{
bool hasListeners;
}

// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}

// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}

- (void)calendarEventReminderReceived:(NSNotification *)notification
{
NSString *eventName = notification.userInfo[@"name"];
if (hasListeners) {// Only send events if anyone is listening
[self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];
}
}

執行緒

除非原生模組提供自己的方法佇列,否則它不應對它在哪個執行緒上被呼叫做出任何假設。目前,如果原生模組未提供方法佇列,React Native 將為其建立一個單獨的 GCD 佇列,並在那裡調用其方法。請注意,這是一個實作細節,可能會變更。如果您想為原生模組明確提供方法佇列,請在原生模組中覆寫 (dispatch_queue_t) methodQueue 方法。例如,如果它需要使用僅限主執行緒的 iOS API,它應該透過以下方式指定

objectivec
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}

同樣地,如果操作可能需要很長時間才能完成,原生模組可以指定自己的佇列來執行操作。再次強調,目前 React Native 將為您的原生模組提供一個單獨的方法佇列,但這是一個您不應依賴的實作細節。如果您不提供自己的方法佇列,在未來,您的原生模組的長時間執行操作可能會導致在其他不相關的原生模組上執行的異步呼叫被封鎖。例如,此處的 RCTAsyncLocalStorage 模組會建立自己的佇列,因此 React 佇列不會因等待可能緩慢的磁碟存取而被封鎖。

objectivec
- (dispatch_queue_t)methodQueue
{
return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
}

指定的 methodQueue 將由您模組中的所有方法共用。如果您的方法中只有一個方法是長時間執行的(或由於某些原因需要在與其他方法不同的佇列上執行),您可以在方法內部使用 dispatch_async 在另一個佇列上執行該特定方法的程式碼,而不會影響其他方法

objectivec
RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Call long-running code on background thread
...
// You can invoke callback from any thread/queue
callback(@[...]);
});
}

在模組之間共用 dispatch 佇列

methodQueue 方法將在模組初始化時呼叫一次,然後由 React Native 保留,因此您無需自己保留對佇列的參考,除非您希望在模組內使用它。但是,如果您希望在多個模組之間共用相同的佇列,那麼您需要確保您為每個模組保留並傳回相同的佇列實例。

依賴注入

React Native 將自動建立和初始化任何已註冊的原生模組。但是,您可能希望建立和初始化您自己的模組實例,例如,注入依賴項。

您可以透過建立一個實作 RCTBridgeDelegate 協定的類別,使用委派作為參數初始化 RCTBridge,並使用初始化的橋接器初始化 RCTRootView 來執行此操作。

objectivec
id<RCTBridgeDelegate> moduleInitialiser = [[classThatImplementsRCTBridgeDelegate alloc] init];

RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:moduleInitialiser launchOptions:nil];

RCTRootView *rootView = [[RCTRootView alloc]
initWithBridge:bridge
moduleName:kModuleName
initialProperties:nil];

匯出 Swift

Swift 不支援巨集,因此在 React Native 內部向 JavaScript 公開原生模組及其方法需要更多設定。但是,它的運作方式相對相同。假設您具有相同的 CalendarModule,但作為 Swift 類別

swift
// CalendarModule.swift

@objc(CalendarModule)
class CalendarModule: NSObject {

@objc(addEvent:location:date:)
func addEvent(_ name: String, location: String, date: NSNumber) -> Void {
// Date is ready to use!
}

@objc
func constantsToExport() -> [String: Any]! {
return ["someKey": "someValue"]
}

}

重要的是使用 @objc 修飾符,以確保類別和函數正確匯出到 Objective-C 執行時環境。

然後建立一個私有實作檔案,它將向 React Native 註冊所需資訊

objectivec
// CalendarModuleBridge.m
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_MODULE(CalendarModule, NSObject)

RCT_EXTERN_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)date)

@end

對於 Swift 和 Objective-C 的新手,每當您在 iOS 專案中混合使用這兩種語言時,您還需要一個額外的橋接檔案,稱為橋接標頭,以將 Objective-C 檔案公開給 Swift。如果您透過 Xcode File>New File 選單選項將您的 Swift 檔案新增到您的應用程式,Xcode 將提供為您建立此標頭檔案。您將需要在這個標頭檔案中匯入 RCTBridgeModule.h

objectivec
// CalendarModule-Bridging-Header.h
#import <React/RCTBridgeModule.h>

您也可以使用 RCT_EXTERN_REMAP_MODULE_RCT_EXTERN_REMAP_METHOD 來變更您正在匯出的模組或方法的 JavaScript 名稱。如需更多資訊,請參閱 RCTBridgeModule

製作第三方模組時的重要事項:僅在 Xcode 9 及更高版本中支援使用 Swift 的靜態函式庫。為了讓 Xcode 專案在使用您包含在模組中的 iOS 靜態函式庫中使用 Swift 時進行建置,您的主要應用程式專案必須包含 Swift 程式碼和橋接標頭本身。如果您的應用程式專案不包含任何 Swift 程式碼,一個可行的解決方案可以是單個空的 .swift 檔案和一個空的橋接標頭。

保留方法名稱

invalidate()

原生模組可以透過實作 invalidate() 方法來符合 iOS 上的 RCTInvalidating 協定。當原生橋接器失效時(即:在 devmode 重新載入時),可以調用此方法。請根據需要使用此機制來為您的原生模組執行必要的清理。