此文是我的出版書籍《React Native 精解與實戰》連載分享,此書由機械工業出版社出版,書中詳解了 React Native 架構底層原理、React Native 組件布局、組件與 API 的介紹與代碼實戰,以及 React Native 與 iOS、Android 平台的混合開發底層原理講解與代碼實戰示範,精選了大量執行個體代碼,方便讀者快速學習。
書籍還配套了視頻教程「80 節實戰課精通 React Native 開發」,此視頻課程建議配合書籍學習,書籍中原理性的東西講解的比較清晰,而視頻教程對於組件、API 等部分的代碼實戰開發講解比較直觀。
書籍相關所有資料請訪問:http://rn.parryqiu.com
本章將詳細講解在 React Native 架構下,iOS 平台混合開發的原理以及詳細實現方法,並依然通過案例進行實際應用學習。本章需要具備 iOS 平台開發語言 Objective-C 或 Swift 以及 iOS 核心類庫的基本知識。
11.1 iOS 平台混合開發簡介
當我們需要使用一些 iOS 平台的 API,而 React Native 架構目前還沒有提供對應的 JavaScript API 時,我們就需要自己編寫 React Native 架構與 iOS 平台結合的混合開發代碼,使得 React Native 架構可以直接與 iOS 平台的原生代碼進行通訊。
混合開發的其他使用情境還包括對一些現有的 Objective-C、Swift 或者 C++ 代碼進行複用,或者編寫一些用於圖片處理、資料庫或一些其他進階特性的高效能或多線程的代碼。React Native 都開放了對應的介面供開發人員調用。
接下來在原理講解的章節,我們通過結合一個簡單的執行個體詳細講解一下 React Native 中與 iOS 平台混合開發的通訊機制,這部分內容稍顯複雜,可能需要反覆閱讀理解其底層原理,並結合小執行個體進行體會學習,在第二部分還將有一個真實的混合開發樣本,繼續加深你對混合開發的理解。
這部分內容屬於 React Native 開發的高階內容,即使不掌握也不影響你在學習了 React Native 基礎知識後進行 App 的開發,不過理解並掌握了這部分的話,更加有助於你理解 React Native 的底層原理與實現。
11.2 iOS 平台混合開發原理詳解
這一章節,我們通過實現在 React Native 架構中調用我們在 Objective-C 中編寫的原生代碼,Objective-C 中的函數返回了一個簡單的字串,React Native 架構中通過 JavaScript 代碼將字串擷取到在 View 中的 Text 組件上顯示。功能雖然簡單,但是我們主要是通過此簡易功能的實現流程,深入學習 React Native 中與 iOS 平台結合的混合開發原理。
iOS 平台混合開發實現的過程包括如下幾個過程:
- 在 iOS 平台的原生代碼中定義混合開發的入口,用於與 React Native 中的 JavaScript 代碼進行通訊;
- 設定 iOS 平台下項目的編譯必備設定;
- 在 React Native 項目中通過 JavaScript 代碼調用混合開發實現的 iOS 平台原生功能。
完整代碼在本書配套源碼的 11-02 檔案夾。
11.2.1 iOS 原生代碼實現
原生模組使用 Objective-C 的類定義來實現與 React Native 架構通訊的協議介面 RCTBridgeModule,注意 RCT 是 ReaCT 取的幾個大寫字母的縮寫。
首先我們通過 React Native CLI 命令初始化一個空的項目,命令執行 11-1 所示。
圖 11-1 初始化一個空項目
使用 Xcode 開啟 ios 檔案夾下的 xcodeproj 專案檔,後續的混合開發我們將在 Xcode 中進行。
我們將我們混合開發的模組命名為 MyModule,並在 Xcode 中分別建立兩個對應的檔案,一個為標頭檔 MyModule.h,另一個為使用 Objective-C 來實現的類 MyModule.m。建立時可以在 Xcode 建立視窗中選擇檔案類型, 11-2 所示。
圖 11-2 Xcode 下建立檔案選擇類型
標頭檔 MyModule.m 檔案中初始化的代碼使用如下代碼。
1. #import "RCTBridgeModule.h" 2. 3. @interface MyModule : NSObject <RCTBridgeModule> 4. 5. @end
代碼第一行匯入了 React Native 架構與原生代碼通訊的協議標頭檔 RCTBridgeModule.h。
為了通過類對 RCTBridgeModule.h 的實現,類中還需要包含 RCT_EXPORT_MODULE() 的宏定義,RCT_EXPORT_MODULE() 中還可以傳入參數,命名自訂原生組件的名稱,如我們之前定義的檔案名稱為 MyModule,這裡可以通過傳遞參數重新定義模組名稱,RCT_EXPORT_MODULE(RenameMyModule) 這樣就將匯出的模組命名成了 RenameMyModule。如果不傳遞參數,那麼就使用類檔案的名稱,即 MyModule.m 的名稱 MyModule。如果模組類檔案包含 RCT 開頭的檔案名稱,那麼最終的模組名稱將自動不包含 RCT 字串。
MyModule.m 檔案中的代碼實現如下。
1. #import "MyModule.h" 2. 3. @implementation MyModule 4. 5. // 需要包含的宏定義 6. RCT_EXPORT_MODULE() 7. 8. // 定義了一個返回的字串 9. - (NSDictionary *)constantsToExport { 10. return @{@"hello": @"你好,這是我編寫的第一個 iOS 原生模組!"}; 11. } 12. 13. // 定義了一個可被調用的函數 14. RCT_EXPORT_METHOD(squareMe:(NSString *)number:(RCTResponseSenderBlock)callback) { 15. int num = [number intValue]; 16. callback(@[[NSNull null], [NSNumber numberWithInt:(num*num)]]); 17. } 18. 19. @end
此段代碼定義了一個固定的字串輸出,方法為 constantsToExport,返回了名稱為 hello 的字串。第二個定義了一個可以供 React Native 的 JavaScript 代碼調用的函數,函數功能非常簡單,就是將傳遞過來的 int 參數進行平方計算,計算後將計算的值返回。
函數的定義需要使用宏命令 RCT_EXPORT_METHOD 進行顯式地定義。定義的函數都會被非同步地調用,所以函數的定義不是直接使用 return 返回一個值,和固定的字串返回不一樣。
squareMe 是定義了此函數的函數名稱,參數為一個 NSString 型的值,名稱為 number,另一個參數為函數的回呼函數,用於擷取原生代碼的執行結果。
函數的 callback 第一個參數為錯誤的返回(這裡沒有錯誤就返回了一個 null),第二個為計算後的值,供函數回調使用。
11.2.2 iOS 項目編譯設定
如上代碼都編寫完成後,在 Xcode 中執行項目編譯,點擊 Xcode 中的 Build 命令, 11-3 所示。
圖 11-3 Xcode 項目的編譯
如果在編譯時間遇到 fatal error: 'RCTBridgeModule.h' file not found 的錯誤,即 'RCTBridgeModule.h 檔案找不到的問題,錯誤 11-4 所示。
圖 11-4 RCTBridgeModule.h 檔案找不到的錯誤
解決的方法為在 Xcode 的項目設定“Build Settings”選項卡下找到“Header Search Paths”設定節點,並確認在其中包含了 11-5 所示的定義,即添加了 $(SRCROOT)/../node_modules/react-native/React 值的定義並在下拉選項中選擇了“recursive”。
圖 11-5 設定 Xcode 的 Search Paths
進行了如上的設定後,再次編譯項目即可解決此錯誤問題。