iOS - ネイティブモジュールでSwiftを使用する
Swiftは、iOSでネイティブアプリケーションを開発するための公式のデフォルト言語です。
このガイドでは、Swiftを使用してネイティブモジュールを記述する方法について説明します。
React Nativeのコアは主にC++で記述されており、Appleが開発した相互運用性レイヤーにもかかわらず、SwiftとC++の相互運用性はあまり良くありません。
そのため、このガイドで記述するモジュールは、言語間の非互換性により、純粋なSwift実装にはなりません。一部のObjective-C++グルーコードを記述する必要がありますが、このガイドの目的は、必要なObjective-C++コードの量を最小限に抑えることです。レガシーアーキテクチャから新しいアーキテクチャに既存のネイティブモジュールを移行する場合、このアプローチにより、ほとんどのコードを再利用できるはずです。
このガイドは、ネイティブモジュールガイドのiOS実装から始まります。このガイドに入る前に、そのガイドに精通し、可能であればガイドの例を実装していることを確認してください。
アダプターパターン
目標は、すべてのビジネスロジックをSwiftモジュールを使用して実装し、アプリとSwift実装を接続できる薄いObjective-C++グルーレイヤーを持つことです。
アダプターデザインパターンを活用することで、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
ファイルを更新する
次に、RCTNativeLocalStorage
の実装を更新して、Swiftモジュールを作成し、そのメソッドを呼び出せるようにする必要があります。
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
です。
- プロジェクトナビゲーターで、アプリ名(左側の
アプリをビルドして実行する
これで、ネイティブモジュールのガイドの最後のステップに従うことができ、Swiftで記述されたネイティブモジュールが実行されているアプリが表示されるはずです。