ネイティブコンポーネント
独自の種類の CheckBox (Android) や UIButton (iOS) のような ホストコンポーネント をラップする 新しい React Native コンポーネントを構築したい場合は、Fabric Native コンポーネントを使用する必要があります。
このガイドでは、Web ビュー コンポーネントを実装することで、Fabric Native コンポーネントを構築する方法を説明します。その手順は次のとおりです。
- 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
これで、Web ビューをレンダリングできるように 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
イベントの考えられる結果です。- 実際の
OnScriptLoadedEvent
は React Native のEvent
クラスを拡張する必要があります。
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
関数をオーバーライドします。これは、仕様の codegenNativeComponent
関数呼び出しで使用されるのと同じ名前を返す必要があります。
createViewInstance
関数は、新しい ReactWebView
をインスタンス化する責任があります。
次に、ViewManager は、React のコンポーネントのすべてのプロパティがネイティブビューをどのように更新するかを定義する必要があります。この例では、React が WebView
に設定する sourceURL
プロパティをどのように処理するかを決定する必要があります。
最後に、コンポーネントがイベントを発行できる場合、バブリングイベントの場合は getExportedCustomBubblingEventTypeConstants
をオーバーライドし、直接イベントの場合は getExportedCustomDirectEventTypeConstants
をオーバーライドしてイベント名をマッピングする必要があります。
4. ReactWebViewPackage
を記述する
ネイティブモジュールと同様に、ネイティブコンポーネントも 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 がレンダリングする必要があるビューに応じて適切な ViewManager を返します。getReactModuleInfoProvider
は、モジュールをランタイムに登録するときに必要なすべての情報を提供します。
5. アプリケーションに ReactWebViewPackage
を登録する
最後に、アプリケーションに ReactWebViewPackage
を登録する必要があります。これは、MainApplication
ファイルを修正し、ReactWebViewPackage
を getPackages
関数によって返されるパッケージのリストに追加することで行います。
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()
}
}
}
これで、Web ビューをレンダリングできるように iOS プラットフォームコードを記述する時が来ました。従う必要がある手順は次のとおりです。
- Codegen を実行します。
RCTWebView
のコードを記述します。- アプリケーションに
RCTWebView
を登録します。
1. Codegen の実行
手動で Codegen を実行する こともできますが、コンポーネントをデモするアプリケーションを使用してこれを行う方が簡単です。
cd ios
bundle install
bundle exec pod install
重要なのは、Xcode で WebView ネイティブコンポーネントを構築するために使用する Codegen からのログ出力が表示されることです。
生成されたコードをリポジトリにコミットする際は注意が必要です。生成されたコードは React Native の各バージョンに固有です。npm peerDependencies を使用して、React Native のバージョンとの互換性を制限してください。
3. RCTWebView
を記述する
Xcode を使用して iOS プロジェクトを準備するために、次の 5つのステップ を完了する必要があります。
- CocoPods で生成された Xcode ワークスペースを開きます。
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つのプロトコルを実装しています。RCTCustomWebViewViewProtocol
は Codegen によって生成されます。WKNavigationDelegate
は WebKit フレームワークによって提供され、Web ビューのナビゲーションイベントを処理します。
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 固有の機能にアクセスする必要がない場合は、このステップをスキップできます。
Web ビューは、Xcode およびデバイスに付属するフレームワークの1つである WebKit を介して Apple が提供する一部の機能にアクセスする必要があります。これは、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 |
---|---|
![]() | ![]() |