先日とある案件でWebViewを使ったアプリを開発しました。expoでアプリの開発をしましたが、普通とちょっと違うのは大半をwebviewで作成したところになると思います。
今回の記事ではWebViewベースでアプリ開発することの良かったところ悪かったところを書きたいと思います。
そもそもWebViewとは
ウェブコンテンツをアプリに埋め込んで表示する機能です。アプリにいながらブラウザ内のページを表示することができます。機能やスタイルはスマホアプリにも対応できるように作っていればそのままアプリ内でもブラウザと同じことをすることができます。
import { WebView, WebViewMessageEvent } from 'react-native-webview';
~~~~
return (
<WebView
source={{
uri,
}}
style={styles.webview}
javaScriptEnabled={true}
onMessage={onMessage}
injectedJavaScript={injectedJavaScript}
ref={webViewRef}
nestedScrollEnabled={false}
overScrollMode="never"
onError={handleWebViewError}
onHttpError={handleWebViewHttpError}
onRenderProcessGone={handleReload}
onContentProcessDidTerminate={handleReload}
/>
)
アプリの構成
- expo
- Next.js
- TypeScript
expoでネイティブアプリの部分を開発しました。アプリは会員登録してログインしてアプリのさまざまな機能を利用できます。ログイン画面はネイティブアプリになりますが、ログインした後や会員登録は全てWebViewでNext.jsで開発しています。
有名な使用例ではamazonの商品詳細ページがwebviewで作られておりアプリで見てもブラウザで見ても変わらず同じ見た目になっています。
何でWebViewで作ったのか
今回WebViewでアプリを開発することになった理由ですが、ブラウザでも同じアプリを展開する予定なのでコードを一元管理するためというのが大きな理由です。1箇所改修すればアプリもブラウザもアップデートされるのでメンテナンスが楽になります。
そのためコストを抑えることもできます。
これは自分の想像の部分もあるのですが、営業する時のネタとしても使いやすいのかなと思っています。
ただ後述しますが、デメリットの方が多かったです。。。
WebViewで作って良かったこと
- 大半がwebviewなので更新は楽
webview内の更新だけであればexpoアプリは関係ないのでwebviewコンテンツを更新してデプロイするだけでアプリの審査を受けてバージョンを上げる必要はありません。 - 慣れている技術で開発できた
開発メンバーがブラウザアプリの方が慣れていたのでその技術を使用して開発できやりやすい部分もありました。
WebViewで作って悪かったこと
- WebView起動時は時間がかかる
- オフラインで使えないことがある
- 大規模なアプリの場合は内部品質が下がる
WebViewとネイティブアプリの連携で値の受け渡しが必要になってくることがあります。例えばログインした後にトークンを渡したりカメラを起動させたりなどです。このような機能はNext.js側から何をやっているか見えにくくします。
さらにネイティブ側からWebView側に値を返すためにinjectJavaScriptを行いますが、このイベントを受け取るためにuseEffectを使う必要が出てしまいました。
小規模なアプリや局所的にwebViewを使う場合は問題ないと思いますが、今回のような規模が大きく大半をwebViewで開発していると連携が必要な箇所が多くなり整理するのが大変になりました。
// Next.js
useEffect(() => {
if (typeof window !== 'undefined') {
window.ReactNativeWebView?.postMessage("DOWNLOAD_PDF");
}
if (isWebView()) {
const handleMessage = (event: MessageEvent) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'DOWNLOAD_PDF') {
doSomeThing
}
} catch (error) {
console.error('Error parsing message:', error);
}
};
window.addEventListener('message', handleMessage);
// クリーンアップ関数
return () => {
window.removeEventListener('message', handleMessage);
};
}
}, []);
// WebViewに成功を通知
webViewRef.current?.injectJavaScript(`
window.postMessage(JSON.stringify({
type: 'DOWNLOAD_PDF_SUCCESS',
fileName: '${safeFileName}'
}), '*');
true;
`);
return (
<WebViewContainer
~~~~
onMessage={handleMessage}
injectedJavaScript={injectedJavaScript}
/>
)
- 画面遷移が大変なことがある
設計にもよりますが、webViewであるフローの途中に一度ネイティブの別のページを表示し、その後webViewに戻ってきて続きのフローを行う仕様が追加されました。webviewが一度閉じてしまうため状態を保持しておくことが必要になりました。
保存しておきたいデータが多い場合はSecureStoreを使っていたのでここに一度保存するか、少ない場合は画面遷移のイベントをネイティブ側に送る際に一緒にデータも送り、データを持ち歩くような形にするようにしました。
webview → ネイティブ → webView(遷移前の状態)
-
スマホの機能をそのまま使えない
スマホのface IDやOCR機能、pdfファイルのダウンロードなどWebView側ではできない機能があるのでその度にわざわざネイティブ側にイベントを送らないといけません。 -
他のアプリなどを裏でたくさん開いたりした際に画面が真っ白になる
スマホで別アプリをたくさん開いた後に開発中のアプリに戻ってくるとWebView画面が真っ白になります。これはメモリが解放されていることが原因なのではないかと思っているのですが、真っ白の場合はWebViewをリロードさせる必要がありました。
結論
今回webviewをベースにネイティブアプリを開発して正直かなり苦労しました。
自分自身ネイティブアプリの開発はまだまだ慣れていないといったところもありますが、WebViewでアプリみたいなことをそのままできないという点や値の連携が大変といった点に苦しめられました。
ネイティブアプリの大半をWebViewで開発することは個人的にはあまりおすすめはしないです。Amazonの詳細ページのみWebViewにしている例のように、局所的にWebViewにしてブラウザとスマホアプリで同じ画面を見られるようにするといった使い方や小規模のアプリでWebViewを使うのはいいと思いました。