ネイティブコンポーネント
Android の独自の CheckBox や iOS の UIButton のような Host Component をラップする新しい React Native コンポーネントを構築したい場合は、Fabric Native Component を使用する必要があります。
このガイドでは、Web ビュー コンポーネントを実装することで、Fabric Native Component の構築方法を説明します。その手順は次のとおりです。
- Flow または TypeScript を使用して JavaScript 仕様を定義します。
- 提供された仕様からコードを生成し、自動リンクされるように依存関係管理システムを設定します。
- ネイティブ コードを実装します。
- アプリケーションでコンポーネントを使用します。
コンポーネントを使用するには、プレーンテンプレートで生成されたアプリケーションが必要です。
npx @react-native-community/cli@latest init Demo --install-pods false
WebView コンポーネントを作成する
このガイドでは、Web View コンポーネントの作成方法を説明します。Android の WebView コンポーネントと iOS の WKWebView コンポーネントを使用して、コンポーネントを作成します。
まず、コンポーネントのコードを格納するフォルダー構造を作成しましょう。
mkdir -p Demo/{specs,android/app/src/main/java/com/webview}
これにより、作業する以下のレイアウトが得られます。
Demo
├── android/app/src/main/java/com/webview
└── ios
└── specs
android/app/src/main/java/com/webviewフォルダーは、Android コードを格納するフォルダーです。iosフォルダーは、iOS コードを格納するフォルダーです。specsフォルダーは、Codegen の仕様ファイルを格納するフォルダーです。
1. Codegen の仕様を定義する
仕様は、TypeScript または Flow のいずれかで定義する必要があります(詳細については Codegen のドキュメントを参照してください)。これは Codegen によって使用され、C++、Objective-C++、および Java を生成して、プラットフォーム コードを React が実行される JavaScript ランタイムに接続します。
仕様ファイルは、Codegen で動作するように <MODULE_NAME>NativeComponent.{ts|js} という名前である必要があります。サフィックス NativeComponent は単なる慣例ではなく、実際に Codegen によって仕様ファイルを検出するために使用されます。
WebView コンポーネントにこの仕様を使用します。
- TypeScript
- Flow
import type {
CodegenTypes,
HostComponent,
ViewProps,
} from 'react-native';
import {codegenNativeComponent} from 'react-native';
type WebViewScriptLoadedEvent = {
result: 'success' | 'error';
};
export interface NativeProps extends ViewProps {
sourceURL?: string;
onScriptLoaded?: CodegenTypes.BubblingEventHandler<WebViewScriptLoadedEvent> | null;
}
export default codegenNativeComponent<NativeProps>(
'CustomWebView',
) as HostComponent<NativeProps>;
// @flow strict-local
import type {CodegenTypes, HostComponent, ViewProps} from 'react-native';
import {codegenNativeComponent} from 'react-native';
type WebViewScriptLoadedEvent = $ReadOnly<{|
result: "success" | "error",
|}>;
type NativeProps = $ReadOnly<{|
...ViewProps,
sourceURL?: string;
onScriptLoaded?: CodegenTypes.BubblingEventHandler<WebViewScriptLoadedEvent>?;
|}>;
export default (codegenNativeComponent<NativeProps>(
'CustomWebView',
): HostComponent<NativeProps>);
この仕様は、インポートを除いて、3つの主要な部分で構成されています。
WebViewScriptLoadedEventは、イベントがネイティブから JavaScript に渡す必要のあるデータをサポートするデータ型です。NativePropsは、コンポーネントに設定できるプロパティの定義です。codegenNativeComponentステートメントにより、カスタム コンポーネントのコードを自動生成でき、ネイティブ実装と一致するために使用されるコンポーネントの名前を定義します。
Native Modules と同様に、specs/ ディレクトリに複数の仕様ファイルを含めることができます。使用できる型と、これらがマップするプラットフォームの型に関する詳細については、付録を参照してください。
2. Codegen を実行するように構成する
この仕様は、React Native の Codegen ツールによって使用され、プラットフォーム固有のインターフェースとボイラープレートを生成します。これを行うには、Codegen は仕様を見つける場所と、それを使って何をすべきかを知る必要があります。package.json を更新して、以下を含めます。
"start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "AppSpec",
"type": "components",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.webview"
},
"ios": {
"componentProvider": {
"CustomWebView": "RCTWebView"
}
}
},
"dependencies": {
Codegen のすべての設定が完了したら、生成されたコードにフックするためにネイティブ コードを準備する必要があります。
iOS の場合、仕様によってエクスポートされた JS コンポーネントの名前 (CustomWebView) と、コンポーネントをネイティブに実装する iOS クラスを宣言的にマッピングしていることに注意してください。
2. ネイティブコードのビルド
これでネイティブプラットフォームコードを記述する時間です。これにより、React がビューをレンダリングする必要があるときに、プラットフォームが正しいネイティブビューを作成し、画面にレンダリングできるようになります。
Android と iOS の両方のプラットフォームで作業する必要があります。
このガイドでは、新しいアーキテクチャのみで動作するネイティブコンポーネントを作成する方法を示します。新しいアーキテクチャと従来のアーキテクチャの両方をサポートする必要がある場合は、後方互換性ガイドを参照してください。
- Android
- iOS
これで、ウェブビューをレンダリングできるように、Android プラットフォームコードをいくつか記述する時間です。従う必要がある手順は次のとおりです。
- Codegen の実行
ReactWebViewのコードを記述するReactWebViewManagerのコードを記述するReactWebViewPackageのコードを記述する- アプリケーションに
ReactWebViewPackageを登録する
1. Gradle を介して Codegen を実行する
選択した IDE が使用できるボイラープレートを生成するために、これを一度実行します。
cd android
./gradlew generateCodegenArtifactsFromSchema
Codegen は、実装する必要がある ViewManager インターフェースと、Web ビューの ViewManager デリゲートを生成します。
2. ReactWebView を記述する
ReactWebView は、カスタム コンポーネントを使用するときに React Native がレンダリングする Android ネイティブ ビューをラップするコンポーネントです。
android/src/main/java/com/webview フォルダーに、このコードを含む ReactWebView.java または ReactWebView.kt ファイルを作成します。
- Java
- Kotlin
package com.webview;
import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.events.Event;
public class ReactWebView extends WebView {
public ReactWebView(Context context) {
super(context);
configureComponent();
}
public ReactWebView(Context context, AttributeSet attrs) {
super(context, attrs);
configureComponent();
}
public ReactWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
configureComponent();
}
private void configureComponent() {
this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
this.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
emitOnScriptLoaded(OnScriptLoadedEventResult.success);
}
});
}
public void emitOnScriptLoaded(OnScriptLoadedEventResult result) {
ReactContext reactContext = (ReactContext) context;
int surfaceId = UIManagerHelper.getSurfaceId(reactContext);
EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId());
WritableMap payload = Arguments.createMap();
payload.putString("result", result.name());
OnScriptLoadedEvent event = new OnScriptLoadedEvent(surfaceId, getId(), payload);
if (eventDispatcher != null) {
eventDispatcher.dispatchEvent(event);
}
}
public enum OnScriptLoadedEventResult {
success,
error
}
private class OnScriptLoadedEvent extends Event<OnScriptLoadedEvent> {
private final WritableMap payload;
OnScriptLoadedEvent(int surfaceId, int viewId, WritableMap payload) {
super(surfaceId, viewId);
this.payload = payload;
}
@Override
public String getEventName() {
return "onScriptLoaded";
}
@Override
public WritableMap getEventData() {
return payload;
}
}
}
package com.webview
import android.content.Context
import android.util.AttributeSet
import android.webkit.WebView
import android.webkit.WebViewClient
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.events.Event
class ReactWebView: WebView {
constructor(context: Context) : super(context) {
configureComponent()
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
configureComponent()
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
configureComponent()
}
private fun configureComponent() {
this.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
this.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
emitOnScriptLoaded(OnScriptLoadedEventResult.success)
}
}
}
fun emitOnScriptLoaded(result: OnScriptLoadedEventResult) {
val reactContext = context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
val payload =
Arguments.createMap().apply {
putString("result", result.name)
}
val event = OnScriptLoadedEvent(surfaceId, id, payload)
eventDispatcher?.dispatchEvent(event)
}
enum class OnScriptLoadedEventResult {
success,
error;
}
inner class OnScriptLoadedEvent(
surfaceId: Int,
viewId: Int,
private val payload: WritableMap
) : Event<OnScriptLoadedEvent>(surfaceId, viewId) {
override fun getEventName() = "onScriptLoaded"
override fun getEventData() = payload
}
}
ReactWebView は Android の WebView を拡張しているため、プラットフォームで既に定義されているすべてのプロパティを簡単に再利用できます。
このクラスは、3つの Android コンストラクタを定義していますが、実際の実装はプライベートな configureComponent 関数に委ねています。この関数は、コンポーネント固有のすべてのプロパティを初期化します。この場合、WebView のレイアウトを設定し、WebView の動作をカスタマイズするために使用する WebClient を定義しています。このコードでは、ReactWebView は WebClient の onPageFinished メソッドを実装することで、ページの読み込みが完了したときにイベントを発行します。
コードは、実際にイベントを発行するためのヘルパー関数を定義しています。イベントを発行するには、次の手順を実行する必要があります。
ReactContextへの参照を取得します。- 表示しているビューの
surfaceIdを取得します。 - ビューに関連付けられた
eventDispatcherへの参照を取得します。 WritableMapオブジェクトを使用してイベントのペイロードを作成します。- JavaScript に送信する必要があるイベント オブジェクトを作成します。
eventDispatcher.dispatchEventを呼び出してイベントを送信します。
ファイルの最後の部分は、イベントを送信するために必要なデータ型の定義を含んでいます。
OnScriptLoadedEventResultは、OnScriptLoadedイベントの考えられる結果です。- React Native の
Eventクラスを拡張する必要がある実際のOnScriptLoadedEventです。
3. WebViewManager を記述する
WebViewManager は、React Native ランタイムをネイティブ ビューに接続するクラスです。
React は、特定のコンポーネントをレンダリングするようアプリから指示を受け取ると、登録されたビューマネージャーを使用してビューを作成し、必要なすべてのプロパティを渡します。
これが ReactWebViewManager のコードです。
- Java
- Kotlin
package com.webview;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.viewmanagers.CustomWebViewManagerInterface;
import com.facebook.react.viewmanagers.CustomWebViewManagerDelegate;
import java.util.HashMap;
import java.util.Map;
@ReactModule(name = ReactWebViewManager.REACT_CLASS)
class ReactWebViewManager extends SimpleViewManager<ReactWebView> implements CustomWebViewManagerInterface<ReactWebView> {
private final CustomWebViewManagerDelegate<ReactWebView, ReactWebViewManager> delegate =
new CustomWebViewManagerDelegate<>(this);
@Override
public ViewManagerDelegate<ReactWebView> getDelegate() {
return delegate;
}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public ReactWebView createViewInstance(ThemedReactContext context) {
return new ReactWebView(context);
}
@ReactProp(name = "sourceUrl")
@Override
public void setSourceURL(ReactWebView view, String sourceURL) {
if (sourceURL == null) {
view.emitOnScriptLoaded(ReactWebView.OnScriptLoadedEventResult.error);
return;
}
view.loadUrl(sourceURL, new HashMap<>());
}
public static final String REACT_CLASS = "CustomWebView";
@Override
public Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
Map<String, Object> map = new HashMap<>();
Map<String, Object> bubblingMap = new HashMap<>();
bubblingMap.put("phasedRegistrationNames", new HashMap<String, String>() {{
put("bubbled", "onScriptLoaded");
put("captured", "onScriptLoadedCapture");
}});
map.put("onScriptLoaded", bubblingMap);
return map;
}
}
package com.webview
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.viewmanagers.CustomWebViewManagerInterface;
import com.facebook.react.viewmanagers.CustomWebViewManagerDelegate;
@ReactModule(name = ReactWebViewManager.REACT_CLASS)
class ReactWebViewManager(context: ReactApplicationContext) : SimpleViewManager<ReactWebView>(), CustomWebViewManagerInterface<ReactWebView> {
private val delegate: CustomWebViewManagerDelegate<ReactWebView, ReactWebViewManager> =
CustomWebViewManagerDelegate(this)
override fun getDelegate(): ViewManagerDelegate<ReactWebView> = delegate
override fun getName(): String = REACT_CLASS
override fun createViewInstance(context: ThemedReactContext): ReactWebView = ReactWebView(context)
@ReactProp(name = "sourceUrl")
override fun setSourceURL(view: ReactWebView, sourceURL: String?) {
if (sourceURL == null) {
view.emitOnScriptLoaded(ReactWebView.OnScriptLoadedEventResult.error)
return;
}
view.loadUrl(sourceURL, emptyMap())
}
companion object {
const val REACT_CLASS = "CustomWebView"
}
override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any> =
mapOf(
"onScriptLoaded" to
mapOf(
"phasedRegistrationNames" to
mapOf(
"bubbled" to "onScriptLoaded",
"captured" to "onScriptLoadedCapture"
)))
}
ReactWebViewManager は React の SimpleViewManager クラスを拡張し、Codegen によって生成された CustomWebViewManagerInterface を実装します。
Codegen によって生成された別の要素である CustomWebViewManagerDelegate の参照を保持します。
次に、getName 関数をオーバーライドします。これは、spec の codegenNativeComponent 関数呼び出しで使用されたのと同じ名前を返す必要があります。
createViewInstance 関数は、新しい ReactWebView をインスタンス化する役割を担います。
次に、ViewManager は、React のコンポーネントのすべてのプロパティがネイティブ ビューをどのように更新するかを定義する必要があります。この例では、React が WebView に設定する sourceURL プロパティをどのように処理するかを決定する必要があります。
最後に、コンポーネントがイベントを発行できる場合は、バブリング イベントの場合は getExportedCustomBubblingEventTypeConstants をオーバーライドし、直接イベントの場合は getExportedCustomDirectEventTypeConstants をオーバーライドして、イベント名をマッピングする必要があります。
4. ReactWebViewPackage を記述する
Native Modules と同様に、Native Components も ReactPackage クラスを実装する必要があります。これは、React Native ランタイムにコンポーネントを登録するために使用できるオブジェクトです。
これが ReactWebViewPackage のコードです。
- Java
- Kotlin
package com.webview;
import com.facebook.react.BaseReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.uimanager.ViewManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ReactWebViewPackage extends BaseReactPackage {
@Override
public List<ViewManager<?, ?>> createViewManagers(ReactApplicationContext reactContext) {
return Collections.singletonList(new ReactWebViewManager(reactContext));
}
@Override
public NativeModule getModule(String s, ReactApplicationContext reactApplicationContext) {
if (ReactWebViewManager.REACT_CLASS.equals(s)) {
return new ReactWebViewManager(reactApplicationContext);
}
return null;
}
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> getReactModuleInfos() {
Map<String, ReactModuleInfo> map = new HashMap<>();
map.put(ReactWebViewManager.REACT_CLASS, new ReactModuleInfo(
ReactWebViewManager.REACT_CLASS, // name
ReactWebViewManager.REACT_CLASS, // className
false, // canOverrideExistingModule
false, // needsEagerInit
false, // isCxxModule
true // isTurboModule
));
return map;
}
};
}
}
package com.webview
import com.facebook.react.BaseReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.facebook.react.uimanager.ViewManager
class ReactWebViewPackage : BaseReactPackage() {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return listOf(ReactWebViewManager(reactContext))
}
override fun getModule(s: String, reactApplicationContext: ReactApplicationContext): NativeModule? {
when (s) {
ReactWebViewManager.REACT_CLASS -> ReactWebViewManager(reactApplicationContext)
}
return null
}
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
mapOf(ReactWebViewManager.REACT_CLASS to ReactModuleInfo(
name = ReactWebViewManager.REACT_CLASS,
className = ReactWebViewManager.REACT_CLASS,
canOverrideExistingModule = false,
needsEagerInit = false,
isCxxModule = false,
isTurboModule = true,
)
)
}
}
ReactWebViewPackage は BaseReactPackage を拡張し、コンポーネントを適切に登録するために必要なすべてのメソッドを実装します。
createViewManagersメソッドは、カスタム ビューを管理するViewManagerを作成するファクトリ メソッドです。getModuleメソッドは、React Native がレンダリングする必要がある View に応じて適切な ViewManager を返します。getReactModuleInfoProviderは、ランタイムにモジュールを登録するときに必要なすべての情報を提供します。
5. アプリケーションに ReactWebViewPackage を登録する
最後に、アプリケーションに ReactWebViewPackage を登録する必要があります。これは、MainApplication ファイルを修正し、getPackages 関数によって返されるパッケージのリストに ReactWebViewPackage を追加することで行います。
package com.demo
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader
import com.webview.ReactWebViewPackage
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(ReactWebViewPackage())
}
override fun getJSMainModuleName(): String = "index"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}
override val reactHost: ReactHost
get() = getDefaultReactHost(applicationContext, reactNativeHost)
override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
load()
}
}
}
これで、ウェブビューをレンダリングできるように、iOS プラットフォームコードをいくつか記述する時間です。従う必要がある手順は次のとおりです。
- Codegen の実行。
RCTWebViewのコードを記述する- アプリケーションに
RCTWebViewを登録する
1. Codegen を実行する
Codegen を手動で実行することもできますが、コンポーネントをデモするアプリケーションを使用して実行する方が簡単です。
cd ios
bundle install
bundle exec pod install
重要なことに、Codegen からのログ出力が表示されます。これは、Xcode で WebView ネイティブコンポーネントを構築するために使用します。
生成されたコードをリポジトリにコミットする際は注意が必要です。生成されたコードは、React Native のバージョンごとに異なります。npm の peerDependencies を使用して、React Native のバージョンとの互換性を制限してください。
3. RCTWebView を記述する
Xcode を使用して iOS プロジェクトを準備するには、以下の 5つのステップ を完了する必要があります。
- CocoaPods で生成された Xcode Workspace を開きます。
cd ios
open Demo.xcworkspace
- アプリを右クリックして
New Groupを選択し、新しいグループをWebViewと名付けます。
WebViewグループで、New→File from Templateを作成します。
Objective-C Fileテンプレートを使用し、名前をRCTWebViewとします。
-
ステップ 4 を繰り返し、
RCTWebView.hという名前のヘッダーファイルを作成します。 -
RCTWebView.mをRCTWebView.mmにリネームして、Objective-C++ ファイルにします。
Podfile
...
Demo
├── AppDelegate.swift
...
├── RCTWebView.h
└── RCTWebView.mm
ヘッダーファイルと実装ファイルを作成したら、それらの実装を開始できます。
これが RCTWebView.h ファイルのコードで、コンポーネントインターフェースを宣言します。
#import <React/RCTViewComponentView.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTWebView : RCTViewComponentView
// You would declare native methods you'd want to access from the view here
@end
NS_ASSUME_NONNULL_END
このクラスは、RCTViewComponentView クラスを継承する RCTWebView を定義しています。これはすべてのネイティブ コンポーネントの基底クラスであり、React Native によって提供されます。
実装ファイル (RCTWebView.mm) のコードは次のとおりです。
#import "RCTWebView.h"
#import <react/renderer/components/AppSpec/ComponentDescriptors.h>
#import <react/renderer/components/AppSpec/EventEmitters.h>
#import <react/renderer/components/AppSpec/Props.h>
#import <react/renderer/components/AppSpec/RCTComponentViewHelpers.h>
#import <WebKit/WebKit.h>
using namespace facebook::react;
@interface RCTWebView () <RCTCustomWebViewViewProtocol, WKNavigationDelegate>
@end
@implementation RCTWebView {
NSURL * _sourceURL;
WKWebView * _webView;
}
-(instancetype)init
{
if(self = [super init]) {
_webView = [WKWebView new];
_webView.navigationDelegate = self;
[self addSubview:_webView];
}
return self;
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast<CustomWebViewProps const>(_props);
const auto &newViewProps = *std::static_pointer_cast<CustomWebViewProps const>(props);
// Handle your props here
if (oldViewProps.sourceURL != newViewProps.sourceURL) {
NSString *urlString = [NSString stringWithCString:newViewProps.sourceURL.c_str() encoding:NSUTF8StringEncoding];
_sourceURL = [NSURL URLWithString:urlString];
if ([self urlIsValid:newViewProps.sourceURL]) {
[_webView loadRequest:[NSURLRequest requestWithURL:_sourceURL]];
}
}
[super updateProps:props oldProps:oldProps];
}
-(void)layoutSubviews
{
[super layoutSubviews];
_webView.frame = self.bounds;
}
#pragma mark - WKNavigationDelegate
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Success};
self.eventEmitter.onScriptLoaded(result);
}
- (BOOL)urlIsValid:(std::string)propString
{
if (propString.length() > 0 && !_sourceURL) {
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Error};
self.eventEmitter.onScriptLoaded(result);
return NO;
}
return YES;
}
// Event emitter convenience method
- (const CustomWebViewEventEmitter &)eventEmitter
{
return static_cast<const CustomWebViewEventEmitter &>(*_eventEmitter);
}
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<CustomWebViewComponentDescriptor>();
}
@end
このコードは Objective-C++ で書かれており、さまざまな詳細が含まれています。
@interfaceは2つのプロトコルを実装しています。- Codegen によって生成された
RCTCustomWebViewViewProtocol。 - WebKit フレームワークによって提供される
WKNavigationDelegateは、ウェブビューのナビゲーションイベントを処理します。
- Codegen によって生成された
WKWebViewをインスタンス化し、サブビューに追加し、navigationDelegateを設定するinitメソッド。- コンポーネントのプロパティが変更されたときに React Native によって呼び出される
updatePropsメソッド。 - カスタムビューのレイアウト方法を記述する
layoutSubviewsメソッド。 WKWebViewがページの読み込みを完了したときに何をすべきかを処理できるwebView:didFinishNavigation:メソッド。- プロパティとして受け取った URL が有効かどうかをチェックする
urlIsValid:(std::string)propStringメソッド。 - 厳密に型付けされた
eventEmitterインスタンスを取得するためのユーティリティであるeventEmitterメソッド - Codegen によって生成された
ComponentDescriptorを返すcomponentDescriptorProvider。
WebKit フレームワークを追加する
このステップは、Web ビューを作成している場合にのみ必要です。iOS の Web コンポーネントは、Apple が提供する WebKit フレームワークにリンクする必要があります。コンポーネントが Web 固有の機能にアクセスする必要がない場合は、このステップをスキップできます。
ウェブビューは、Apple が Xcode およびデバイスに同梱されているフレームワークの1つである WebKit を介して提供する一部の機能へのアクセスを必要とします。これは、RCTWebView.mm に追加された #import <WebKit/WebKit.h> 行によってネイティブコードで確認できます。
アプリに WebKit フレームワークをリンクするには、次の手順に従います。
- Xcode でプロジェクトをクリックします。
- アプリターゲットを選択します。
- 「General」タブを選択します。
- 「Frameworks, Libraries, and Embedded Contents」セクションが見つかるまでスクロールし、「+」ボタンを押します。
- 検索バーで WebKit をフィルターします。
- WebKit フレームワークを選択します。
- 「Add」をクリックします。

3. ネイティブコンポーネントを使用する
最後に、新しいコンポーネントをアプリで使用できます。生成された App.tsx を次のように更新します。
import React from 'react';
import {Alert, StyleSheet, View} from 'react-native';
import WebView from './specs/WebViewNativeComponent';
function App(): React.JSX.Element {
return (
<View style={styles.container}>
<WebView
sourceURL="https://react.dokyumento.jp/"
style={styles.webview}
onScriptLoaded={() => {
Alert.alert('Page Loaded');
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
alignContent: 'center',
},
webview: {
width: '100%',
height: '100%',
},
});
export default App;
このコードは、作成した新しい WebView コンポーネントを使用して react.dev ウェブサイトを読み込むアプリを作成します。
アプリは、ウェブページが読み込まれたときにもアラートを表示します。
4. WebView コンポーネントを使用してアプリを実行する
- Android
- iOS
yarn run android
yarn run ios
| Android | iOS |
|---|---|
![]() | ![]() |

