iOS - ネイティブモジュールでSwiftを使用する
Swiftは、iOSでネイティブアプリケーションを開発するための公式かつデフォルトの言語です。
このガイドでは、Swiftを使用してネイティブモジュールを記述する方法を探ります。
React Nativeのコアは主にC++で記述されており、Appleが開発した相互運用性レイヤーがあるにもかかわらず、SwiftとC++の相互運用性はあまり良くありません。
そのため、このガイドで作成するモジュールは、言語間の非互換性のために純粋なSwift実装にはなりません。Objective-C++の接着コードを記述する必要がありますが、このガイドの目標は、必要なObjective-C++コードの量を最小限に抑えることです。既存のネイティブモジュールをレガシーアーキテクチャから新しいアーキテクチャに移行する場合、このアプローチにより、ほとんどのコードを再利用できるはずです。
このガイドは、ネイティブモジュールガイドのiOS実装から始まります。このガイドに入る前に、そのガイドに慣れていることを確認し、場合によってはガイドの例を実装してください。
アダプターパターン
目標は、Swiftモジュールを使用してすべてのビジネスロジックを実装し、Objective-C++でアプリとSwift実装を接続できる薄い接着レイヤーを持つことです。
これは、アダプターデザインパターンを活用することで、SwiftモジュールとObjective-C++レイヤーを接続することで実現できます。
Objective-C++オブジェクトはReact Nativeによって作成され、Swiftモジュールへの参照を保持し、そのライフサイクルを管理します。Objective-C++オブジェクトは、すべてのメソッド呼び出しをSwiftに転送します。
Swiftモジュールの作成
最初のステップは、実装をObjective-C++レイヤーからSwiftレイヤーに移動することです。
それを達成するには、次の手順に従ってください。
- Xcodeプロジェクトに新しい空のファイルを作成し、
NativeLocalStorage.swiftという名前を付けます。 - 次のようにSwiftモジュールに実装を追加します。
import Foundation
@objc public class NativeLocalStorage: NSObject {
let userDefaults = UserDefaults(suiteName: "local-storage");
@objc public func getItem(for key: String) -> String? {
return userDefaults?.string(forKey: key)
}
@objc public func setItem(for key: String, value: String) {
userDefaults?.set(value, forKey: key)
}
@objc public func removeItem(for key: String) {
userDefaults?.removeObject(forKey: key)
}
@objc public func clear() {
userDefaults?.dictionaryRepresentation().keys.forEach { removeItem(for: $0) }
}
}
Objective-Cから呼び出す必要があるすべてのメソッドをpublicとして@objcアノテーションを付けて宣言する必要があることに注意してください。また、クラスがNSObjectを継承するようにしてください。そうしないと、Objective-Cから使用できなくなります。
RCTNativeLocalStorageファイルを更新する
次に、Swiftモジュールを作成し、そのメソッドを呼び出すことができるように、RCTNativeLocalStorageの実装を更新する必要があります。
RCTNativeLocalStorage.mmファイルを開きます。- 次のように更新します。
// RCTNativeLocalStorage.m
// TurboModuleExample
#import "RCTNativeLocalStorage.h"
+#import "SampleApp-Swift.h"
- static NSString *const RCTNativeLocalStorageKey = @"local-storage";
-@interface RCTNativeLocalStorage()
-@property (strong, nonatomic) NSUserDefaults *localStorage;
-@end
-@implementation RCTNativeLocalStorage
+@implementation RCTNativeLocalStorage {
+ NativeLocalStorage *storage;
+}
-RCT_EXPORT_MODULE(NativeLocalStorage)
- (id) init {
if (self = [super init]) {
- _localStorage = [[NSUserDefaults alloc] initWithSuiteName:RCTNativeLocalStorageKey];
+ storage = [NativeLocalStorage new];
}
return self;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
return std::make_shared<facebook::react::NativeLocalStorageSpecJSI>(params);
}
- (NSString * _Nullable)getItem:(NSString *)key {
- return [self.localStorage stringForKey:key];
+ return [storage getItemFor:key];
}
- (void)setItem:(NSString *)value key:(NSString *)key {
- [self.localStorage setObject:value forKey:key];
+ [storage setItemFor:key value:value];
}
- (void)removeItem:(NSString *)key {
- [self.localStorage removeObjectForKey:key];
+ [storage removeItemFor:key];
}
- (void)clear {
- NSDictionary *keys = [self.localStorage dictionaryRepresentation];
- for (NSString *key in keys) {
- [self removeItem:key];
- }
+ [storage clear];
}
++ (NSString *)moduleName
+{
+ return @"NativeLocalStorage";
+}
@end
コードは実際には変更されていません。NSUserDefaultsへの参照を直接作成する代わりに、swift実装を使用して新しいNativeLocalStorageを作成し、ネイティブモジュール関数が呼び出されるたびに、その呼び出しはSwiftで実装されたNativeLocalStorageに転送されます。
"SampleApp-Swift.h"ヘッダーをインポートすることを忘れないでください。これはXcodeによって自動生成されるヘッダーで、Objective-Cで利用できる形式でSwiftファイルの公開APIが含まれています。ヘッダーのSampleApp部分は実際にはアプリの名前なので、SampleAppとは異なる名前でアプリを作成した場合、変更する必要があります。
また、ここで説明されているように、ネイティブモジュールはpackage.jsonを使用して登録されるため、RCT_EXPORT_MODULEマクロは不要になったことにも注意してください。
このアプローチはインターフェースに多少のコード重複をもたらしますが、最小限の追加労力で、コードベースにすでに存在するSwiftコードを再利用できます。
ブリッジヘッダーの実装
ライブラリの作者で、別のライブラリとして配布されるネイティブモジュールを開発している場合、この手順は不要です。
SwiftコードをObjective-C++側と接続するために最後に必要な手順は、ブリッジヘッダーです。
ブリッジヘッダーは、Swiftコードから参照する必要があるすべてのObjective-Cヘッダーファイルをインポートできるヘッダーです。
コードベースにすでにブリッジヘッダーがあるかもしれませんが、ない場合は、次の手順に従って新しいものを作成できます。
- Xcodeで新しいファイルを作成し、
"SampleApp-Bridging-Header.h"という名前を付けます。 "SampleApp-Bridging-Header.h"の内容を次のように更新します。
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
+ #import <React-RCTAppDelegate/RCTDefaultReactNativeFactoryDelegate.h>
- プロジェクトでブリッジヘッダーをリンクする
- プロジェクトナビゲーターで、アプリ名(左側の
SampleApp)を選択します。 Build Settingsをクリックします。"Bridging Header"でフィルターします。- 「Bridging Header」への相対パスを追加します。この例では
SampleApp-Bridging-Header.hです。
- プロジェクトナビゲーターで、アプリ名(左側の

アプリをビルドして実行する
これで、Native Module's guideの最後のステップに従うことができ、Swiftで記述されたNative Moduleが実行されているアプリが表示されるはずです。