React NativeのWebビューでデータのやりとり

2022/05/20,

はじめに

こんにちは、おーびるです。

React Nativeではウェブサイトをアプリ内で表示するreact-native-webviewがあります。

https://github.com/react-native-webview/react-native-webview

アプリとWEBサイト間のデータのやりとりについて覚え書きを書きます。

インストール

$ yarn add react-native-webview

iOS

pod install

Android

android/gradle.propertiesに以下を追記

android.useAndroidX=true
android.enableJetifier=true

実装

WebViewコンポーネントのsourceにURLを指定することでサイトを表示できます。

import React, { Component } from 'react';
import { WebView } from 'react-native-webview';

class MyWeb extends Component {
  render() {
    return (
      <WebView
        source={{ uri: 'https://infinite.red' }}
      />
    );
  }
}

Webサイトとアプリの連携

参考:Communicating between JS and Native

アプリからWebサイトを操作する

injectedJavaScriptBeforeContentLoadedに文字列形式で記述されたJavaScriptをわたすことで、Webページを開いたときに任意のJavaScriptを実行できます。

import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  render() {
    const runFirst = `
      window.isNativeApp = true;
      true; // note: this is required, or you'll sometimes get silent failures
    `;
    return (
      <View style={{ flex: 1 }}>
        <WebView
          source={{
            uri: 'https://github.com/react-native-webview/react-native-webview',
          }}
          injectedJavaScriptBeforeContentLoaded={runFirst}
        />
      </View>
    );
  }
}

上記サンプルのようにグローバル変数を定義しておくと、WebサイトのJavaScriptからも利用できます。

window.isNativeApp = true

Webサイトからアプリに情報伝達

Webサイトからアプリに情報を伝達したい場合、window.ReactNativeWebView.postMessageを利用します。

ReactNativeのWebビューからWebサイトにアクセスしたとき、JavaScriptのグローバル変数にwindow.ReactNativeWebView.postMessageが定義されます。

WEBサイト側で以下のようなコードを用意すると、アプリにデータを渡せます。受け付けるデータはstringだけになっています。

if (window.ReactNativeWebView !== undefined) {
	window.ReactNativeWebView.postMessage('Hello')
}

window.ReactNativeWebView.postMessage()で渡されたデータはWebViewコンポーネントのonMessageで受け取れます。event.nativeEvent.dataにデータが入っています。

import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  render() {
    const html = `
      <html>
      <head></head>
      <body>
        <script>
          setTimeout(function () {
            window.ReactNativeWebView.postMessage("Hello!")
          }, 2000)
        </script>
      </body>
      </html>
    `;

    return (
      <View style={{ flex: 1 }}>
        <WebView
          source={{ html }}
          onMessage={(event) => {
            alert(event.nativeEvent.data);
          }}
        />
      </View>
    );
  }
}

セキュリティ回りの配慮

WebViewのonMessageでウェブサイトから実行されたwindow.ReactNativeWebView.postMessage()のデータを取得できますが、送信元をチェックしないとセキュリティの脆弱性になりえます。

window.postMessageの説明になりますが、考慮事項、対処方法は参考になるので以下の項目をお読みになることをおすすめします。

https://developer.mozilla.org/ja/docs/Web/API/Window/postMessage#security_concerns

window.ReactNativeWebView.postMessageの送信元が正規サイトかどうかチェックしましょう。Webビューでページを遷移しているうちに正規サイトから外れてしまう可能性もあるからです。event.nativeEvent.urlで呼び出し元のURLを取得できます。

import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  render() {
	// 正規サイトのURL 	  
    const url = 'https://example.com/'
    return (
      <View style={{ flex: 1 }}>
        <WebView
	      source={{ uri: url }}
          onMessage={(event) => {
            const regex = new RegExp(`^${url}`)
			// 正規サイトじゃなければ早期リターン			
			if (!regex.test(event.nativeEvent.url)) return
			alert(event.nativeEvent.data);	
          }}
        />
      </View>
    );
  }
}

WebViewでアクセスできるサイトを制限する

WebViewでページを開いた場合、制限しなければWebサイトのリンクをたどっていくことで他ドメインのページにもアクセスできます。

それは困りますよね。

アクセスできるページを制限したい場合、originWhitelistで実現できます。originWhitelistで指定されたWebサイト以外は、iOS, Androidのデフォルトブラウザで開くようになります。

import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  render() {
	// 正規サイトのURL 	  
    const url = 'https://example.com/'
    return (
      <View style={{ flex: 1 }}>
        <WebView
	      source={{ uri: url }}
      	  originWhitelist={[url]}
        />
      </View>
    );
  }
}

まとめ

以上、React Nativeのreact-native-webviewについてでした。

Webサイトとモバイルアプリでデータがやりとり方法を知っていると、モバイルアプリからWebサイトを開いたときに自動的にログイン処理を行なうことができたりするので、結構便利だと思います。


この記事はOuvill(おーびる)が書きました。IT関連の記事執筆やサイト作成や、ウェブアプリケーション開発の業務委託などのご依頼を賜っております。

ご要件がある方はコンタクトフォームからご連絡ください。

@Ouvill