見出し画像

React Native の Turbo Native Modules の使い方


前回

1. Turbo Native Modules

Turbo Native Modules」は、「React Nativeアプリ」と「ネイティブコード」を効率的に連携させるための新アーキテクチャです。

2. ネイティブモジュールの呼び出し

2-1. React Nativeプロジェクトの作成

(1) React Nativeプロジェクトの作成。

npx react-native init my_app
cd my_app

2-2. Androidのネイティブモジュールの実装

(1) AndroidStudioでReact Nativeプロジェクトのandroidフォルダを開く。

(2) ネイティブモジュールの実装。

・MyTurboModule.kt

package com.my_app

import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod

// ネイティブモジュール
class MyTurboModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {

    // ネイティブモジュール名の取得
    override fun getName(): String {
        return "MyTurboModule"
    }

    // デバイス名の取得
    @ReactMethod
    fun getDeviceName(promise: Promise) {
        try {
            val deviceName = android.os.Build.MODEL
            promise.resolve(deviceName)
        } catch (e: Exception) {
            promise.reject("Error", e)
        }
    }
}

(3) ネイティブモジュールをJavaScriptから呼び出せるように設定。
「MainApplication.kt」のPackageListに「MyTurboModulePackage」を追加します。

・MyTurboModulePackage.kt

package com.my_app

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.NativeModule
import com.facebook.react.uimanager.ViewManager

// モジュールパッケージ
class MyTurboModulePackage : ReactPackage {

    // ネイティブモジュールの生成
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        return listOf(MyTurboModule(reactContext))
    }

    // ビューマネージャの生成
    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return emptyList()
    }
}

・MainApplication.ktの一部

  override val reactNativeHost: ReactNativeHost =
      object : DefaultReactNativeHost(this) {
        override fun getPackages(): List<ReactPackage> =
            PackageList(this).packages.apply {
              // 自動リンクできないパッケージは、ここで手動で追加できる
              // example: add(MyReactNativePackage())

              add(MyTurboModulePackage()) // ★ここに追加
            }

2-3. iOSのネイティブモジュールの実装

(1) podのインストール。

cd ios
pod install
cd ..

(2) Xcodeで「ios」内のworkspaceを開き、署名し、iPhoneにインストールできることを確認。

(3) ネイティブモジュールの実装。
Swiftでネイティブモジュールを実装します。

・MyNativeModule

import UIKit

// ネイティブモジュール
@objc(MyNativeModule)
class MyNativeModule: NSObject {
  
  // デバイス名の取得
  @objc
  func getDeviceName() -> String {
    return UIDevice.current.name
  }
}


Swiftコードの初回追加時に、ブリッジヘッダー追加ダイアログが開くので、「Create Bridgin Header」ボタンを押します。「<プロジェクト名>-Bridging-Header.h」が生成され、「Build Settings」の「Objective-C Bridging Header」に設定されます。

(4) ネイティブモジュールをJavaScriptから呼び出せるように設定。
<プロジェクト名>にはプロジェクト名を指定してください。

・<プロジェクト名>-Bridging-Header.h

 #import  <React/RCTBridgeModule.h>

・MyTurboModule.h

 #import   <React/RCTBridgeModule.h>

@interface MyTurboModule : NSObject <RCTBridgeModule>
@end

・MyTurboModule.m

 #import  "MyTurboModule.h"
 #import  <React/RCTLog.h>
 #import  <<プロジェクト名>-Swift.h>

@implementation MyTurboModule

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(getDeviceName:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  MyNativeModule *nativeModule = [[MyNativeModule alloc] init];
  resolve([nativeModule getDeviceName]);
}

@end

2-4. ネイティブモジュールの呼び出し

(1) ネイティブモジュールの定義。

・MyTurboModule.ts

import { TurboModule, TurboModuleRegistry } from 'react-native';

// ネイティブモジュールの定義
export interface Spec extends TurboModule {
  getDeviceName(): Promise<string>;
}

export default TurboModuleRegistry.getEnforcing<Spec>('MyTurboModule');

(2) 「App.tsx」の編集。

・App.tsx

import React, { useState } from 'react';
import { Button, Text, SafeAreaView } from 'react-native';
import MyTurboModule from './MyTurboModule';

// アプリ
const App: React.FC = () => {
  const [message, setMessage] = useState('');

  // ボタンクリック時に呼ばれる
  const onClick = () => {
    // デバイス名の取得
    MyTurboModule.getDeviceName()
      .then((deviceName: string) => {
        setMessage(deviceName);
      })
      .catch((error: any) => {
        setMessage(error);
      });    
  };

  return (
    <SafeAreaView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text style={{ fontSize: 24, marginBottom: 20 }}>{message}</Text>
      <Button
        onPress={onClick}
        title="デバイス名の取得"
      />
      </SafeAreaView>
  );
};

export default App;

(3) 実行。

npm start

3. カスタムビューの利用

3-1. React Nativeプロジェクトの作成

(1) React Nativeプロジェクトの作成。

npx react-native init my_app
cd my_app

3-2. Androidのカスタムビューの実装

(1) AndroidStudioでReact Nativeプロジェクトのandroidフォルダを開く。

(2) カスタムビューの実装。
「MainApplication.kt」が存在するフォルダに次のコードを追加します。

・MyCustomView.kt

package com.my_app
import android.content.Context
import android.widget.FrameLayout
import android.widget.TextView
import android.view.Gravity

// カスタムビュー
class MyCustomView(context: Context) : FrameLayout(context) {
    init {
        val textView = TextView(context).apply {
            text = "MyCustomView"
            gravity = Gravity.CENTER
        }
        addView(textView)
    }
}

(3) カスタムビューをJavaScriptから呼び出せるように設定。
「MainApplication.kt」のPackageListに「MyTurboModulePackage」を追加します。

・MyTurboModulePackage.kt

package com.my_app
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewManager

// カスタムビューマネージャ
class MyCustomViewManager : SimpleViewManager<MyCustomView>() {
    override fun getName(): String {
        return "MyCustomView"
    }

    override fun createViewInstance(reactContext: ThemedReactContext): MyCustomView {
        return MyCustomView(reactContext)
    }
}

// モジュールパッケージ
class MyTurboModulePackage : ReactPackage {
    // ネイティブモジュールの生成
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        return emptyList()
    }

    // ビューマネージャの生成
    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return listOf(MyCustomViewManager())
    }
}

・MainApplication.ktの一部

  override val reactNativeHost: ReactNativeHost =
      object : DefaultReactNativeHost(this) {
        override fun getPackages(): List<ReactPackage> =
            PackageList(this).packages.apply {
              // 自動リンクできないパッケージは、ここで手動で追加できる
              // example: add(MyReactNativePackage())

              add(MyTurboModulePackage()) // ★ここに追加
            }

3-3. iOSのカスタムビューの実装

(1) podのインストール。

cd ios
pod install
cd ..

(2) Xcodeで「ios」内のworkspaceを開き、署名し、iPhoneにインストールできることを確認。

(3) カスタムビューの実装。
Swiftでカスタムビューマネージャーを実装します。

・MyCustomViewManager.swift

import UIKit
import React

// カスタムビューマネージャー
@objc(MyCustomViewManager)
class MyCustomViewManager: RCTViewManager {

  // 名前の取得
  override static func moduleName() -> String! {
    return "MyCustomView"
  }
  
  // ビューの取得
  override func view() -> UIView! {
    let view = UIView()

    // ラベル
    let label = UILabel(frame: view.bounds)
    label.text = "MyCustomView"
    label.textAlignment = .center
    label.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(label)
    
    // 制約
    NSLayoutConstraint.activate([
      label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
    ])
    
    return view
  }
}


Swiftコードの初回追加時に、ブリッジヘッダー追加ダイアログが開くので、「Create Bridgin Header」ボタンを押します。「<プロジェクト名>-Bridging-Header.h」が生成され、「Build Settings」の「Objective-C Bridging Header」に設定されます。

(4) カスタムビューをJavaScriptから呼び出せるように設定。
<プロジェクト名>にはプロジェクト名を指定してください。

・<プロジェクト名>-Bridging-Header.h

 #import  "React/RCTViewManager.h"

・MyTurboModule.m

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

@interface RCT_EXTERN_MODULE(MyCustomViewManager, RCTViewManager)
@end

3-4. カスタムビューの利用

(1) カスタムビューの定義。

・MyCustomView.tsx

import { requireNativeComponent, ViewStyle } from 'react-native';

// カスタムビューの定義
interface MyCustomViewProps {
  style?: ViewStyle;
}
const MyCustomView = requireNativeComponent<MyCustomViewProps>('MyCustomView');

export default MyCustomView;

(2) 「App.tsx」の編集。

・App.tsx

import React from 'react';
import { SafeAreaView } from 'react-native';
import MyCustomView from './MyCustomView';

// アプリ
const App: React.FC = () => {
  return (
    <SafeAreaView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <MyCustomView style={{ width: "100%", height: "100%" }} />
    </SafeAreaView>
  );
};

export default App;

(2) 実行。

npm start

4. デバッグ

Android側のデバッグ出力は、通常のAndroid開発と同様、Log.d()で出力してlogcatで確認します。

androud.util.Log.d("my_app", "A>>>");
adb logcat -s my_app

次回



この記事が気に入ったらサポートをしてみませんか?