メインコンテンツへスキップ

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レイヤーに移動することです。

そのためには、次の手順に従ってください。

  1. Xcodeプロジェクトで新しい空のファイルを作成し、NativeLocalStorage.swiftと呼びます。
  2. Swiftモジュールに次のように実装を追加します。
NativeLocalStorage.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モジュールを作成し、そのメソッドを呼び出せるようにする必要があります。

  1. RCTNativeLocalStorage.mmファイルを開きます。
  2. 次のように更新します。
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ヘッダーファイルをインポートできるヘッダーです。

コードベースにすでにブリッジングヘッダーがあるかもしれませんが、ない場合は、次の手順に従って新しいヘッダーを作成できます。

  1. Xcodeで新しいファイルを作成し、"SampleApp-Bridging-Header.h"と呼びます。
  2. "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>
  1. プロジェクトにブリッジングヘッダーをリンクする
    1. プロジェクトナビゲーターで、アプリ名(左側のSampleApp)を選択します。
    2. Build Settingsをクリックします。
    3. "Bridging Header"でフィルタリングします。
    4. 「Bridging Header」への相対パスを追加します。この例ではSampleApp-Bridging-Header.hです。

Bridging Header

アプリをビルドして実行する

これで、ネイティブモジュールのガイドの最後のステップに従うことができ、Swiftで記述されたネイティブモジュールが実行されているアプリが表示されるはずです。