ReactNativeのWebViewからAsset Pack のアセットを使う

TL;DR

  • とあるReactNative (!=expo) アプリのインストールサイズが150MBを超えてしまったので、急遽 Asset Packを使う羽目になった。
  •  deliveryType = "install-time" を適用して分割はできたものの、WebViewから参照するにはどうするんだ?ってなった 。
  •  WebView から参照する目的では 何もしなくてよかった

 

webViewAssetsをベースAPKから分離する・・為にAsset Packを準備する

このアプリでは元々対象ファイルを /webViewAssets/assets/ に配置して /android/app/build.gradle から以下で参照させていました。

android {
  :
  sourceSets {
    main {
      assets.srcDirs = [ "../../webViewAssets/assets" ]
    }
  }
}

 

ネイティブまたは Java 向けのビルド  |  Android デベロッパー  |  Android Developers

に従って /android/webview_assets/src/main/assets/ ディレクトリ (ここに対象ファイルをコピーする) や /android/webview_assets/build.gradle , /android/app/build.gradle を記述します。 

対象ファイルのコピー処理は、gradle でも出来る筈ですが、このアプリは gradle を呼び出すシェルスクリプトがあるのでそこでやりました。要調査です。

 

WebViewから参照する

このアプリでは  WebView#source = { uri: 'file:///android_asset/index.html' } としてアセットにアクセスしていました。このまま動きます。

 

ローカルでAABの動作確認をする

アセット配信をテストする  |  Android デベロッパー  |  Android Developers

の記事に従うと、ビルドした AAB を元にAKPSを生成してローカルインストールできます。
生成したAPKSをunzipすると asset-slices/webview_assets-master.apk なるものがあることも判ります。

 

Play Store の App Bundle エクスプローラーで眺める

内部テストリリースの準備などで Play Store にAABをアップロードすると、App Bundle エクスプローラーの「配信」タブで「機能モジュール」とは別に「Asset packs」に「webview_assets」があることを確認できます。

 

通常のAPKビルドや開発時実行(react-native run-android)と棲み分ける

AABにしたくない状態では Asset Pack 部分は動作しない訳ですが、このアプリでは環境変数で gradle の動作を切り替えることにしました。

export ORG_GRADLE_PROJECT_USE_ASSET_PACK=t

でAsset Packを有効にする事にして、こんな感じ。
/android/app/build.gradle

android {
  :
  sourceSets {
    main {
      if (project.hasProperty('USE_ASSET_PACK')) {
        assets.srcDirs = ["../../webViewAssets/assets"]
      }
    }
  }

  if (project.hasProperty('USE_ASSET_PACK')) {
    assetPacks = [":webview_assets"]
  }
}

 

appium で WebDriverAgentRunner_Runner.app がiOSシミュレータにすらインストールされずにテストできない件

appium を利用してiOSのインテグレーションテストをやっているのですが、ある日動かなくなりました。どうやら WebDriverAgentRunner-Runner.app がシミュレータにインストールされずテストが開始できない模様。

appiumをアプデ(1.x系最新・2.0-beta両方) してもダメでしたが WebDriverAgentRunner-Runner.app を司っている appium-webdriveragent を最新に強制 (3.16.0 → 4.10.15) に強制すると動くようになりました。

package.json に以下を記載です。

"resolutions": {
"appium-webdriveragent": "^4.10.15"
}
}

良かった良かった。

Podfile から Pods.xcodeproj の Build Active Architecture Only を NO

ずっと Intel Mac で ReactNative アプリをビルドしていたのですが Apple Silicon な Mac で react-native run-ios しようとすると以下のようなリンクエラーが山ほど出てしまいました。

building for ios simulator-x86_64 but attempting to link with file built for ios simulator-arm64

自分の場合は原因は2つでした。

 

"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64" している

シミュレータ環境では arm64 をビルドするな、という設定ですね。
指定した理由を思い出せないのですが、消します。

 

Pods に対して Build Active Architecture Only の Debug が YES になっている

ここです。(スクショはNoに変更した後)

XCodeから編集しても pod install すると戻ってしまうので、ios/Podfile で対処します。

target 'awesomeApp' do
:
post_install do |installer|
if RUBY_PLATFORM =~ /\Aarm64/
installer.pods_project
.build_configurations
.find {|conf| conf.name == 'Debug'}
.build_settings['ONLY_ACTIVE_ARCH'] = 'NO'
end
end
end

 

Barrierのサーバー設定で PEM_read_bio:no start line "barrier" エラーになったとき

キーボード/マウスを複数台のPC/Macで共有する Barrier のサーバー設定で、ssh-keygen で生成した Barrier.pem ファイルを C:/Users/<username>/AppData/Local/Barrier/SSL/ にコピーしてもエラーが発生する事があります (自分は発生しました) 。

SSL routines:PEM_read_bio:no start line "barrier"

これについて

개발 도구 – Kyuling Company

https://kyulingcompany.files.wordpress.com/2021/12/2021-12-21-19-31-03.png

openssl req -x509 -nodes -days 365 -subj /CN=Barrier -newkey rsa:4096 -keyout Barrier.pem -out Barrier.pem

と再構成することで治るようです (自分は治りました) 。

正直良くわかりません。

WSL2内サービスをWindows外からアクセスできるようにする

例えば WSL2 の中でsshdを起動してWindows外からアクセスできるようにすると、VSCodeのRemoteDevelop(SSH) でWSL2の中へ飛び込んだり出来て嬉しい(個人差があります)訳ですが、幾つか設定が必要になります。

Windowsホストからであれば localhost でWSL2のサービスへ接続できますが、Windows外からは素の状態ではアクセス出来ません。

sshd (ポート22) を例にすると以下が必要になります。

  1.  Windowsファイアウォール
    詳細は省略。TCPポート22番を許可しましょう。
  2. Windowsホスト→WSL2のポートフォワード
    netsh.exe でポートフォワードを設定します。本稿はこれの説明になります。

netsh.exe によるポートフォワード

Netsh.exe - Win32 apps | Microsoft Docs

こうです。EXE指定するとWSL2からでも実行できます。

netsh.exe interface portproxy add v4tov4 \
listenport=22
listenaddress=0.0.0.0 \
connectport=22
connectaddress=<WSL2-VMのIP>

netsh.exe をスクリプトから管理者権限で実行する

powershellでできます。EXE指定すると(ry

powershell.exe -noprofile -noninteractive \
-command "Start-Process powershell.exe -Verb RunAs -ArgumentList 'netsh.exe ...'

まとめ

WSL2内から実行するnodejsスクリプトとして書くとこうなります。

gist.github.com

 

Gemfileを無視してgem installしたコマンドを使う

cocoapodsが

  • homebrew版は動かず
  • Gemfileに記述した版は ffi がコンフリクトして bundle installできず
  • 普通に gem install した版は動くのでこれを使いたい

ことから調べた。要はプロジェクトの Gemfile を無視してグローバルに gem install したコマンドを使いたいのだけど、普通に叩くとプロジェクトの Gemfile が邪魔をするのでこれを回避する方法。  

方法

$ BUNDLE_GEMFILE=~/ pod install

仕組み

http://ruby.studio-kingdom.com/bundler/bundle_config/ や
https://bundler.io/v2.0/man/bundle-config.1.html によれば
「BUNDLE_GEMFILE で Gemfile が指定されればそれを使い指定されてなければ遡って探す」ので、これで探さなくさせた。

react-native-webView で android/ios共通アセットを表示する

react-nativeプロジェクトに以下を配置してあるとします。

awesomeWebViewApp/
 |- android/
 |- ios/
 |- webViewAssets
 |    |- assets/
 |         |- view1/   //<=これを表示したい
 |             |- index.html
 |             |- bundle.js
 |- App.js
 :

Android向けの準備

android/app/build.gradle に以下を記述します。

  :
android {
    :
    sourceSets {
        main {
            assets.srcDirs = ["../../webViewAssets/assets"]
        }
    }
}

iOS向けの準備

webViewAssets/assets/ を Finder から XCode の Project Navigator (左ペインのプロジェクト階層を表示しているやつ) へドロップして Added folders - Create folder reference
で追加します。
f:id:suzumura_ss:20200217154006p:plain

WebViewへの指定

  • AndroidiOSuri が違うので切り分ける
  • (iOSで) originWhitelist: ['*'] が必要

GitHub - suzumura-ss/awesomeWebViewApp: example of React-Native WebView

import React from 'react';
import {SafeAreaView, StatusBar, Platform} from 'react-native';
import {WebView} from 'react-native-webview';


export default function App () {
  const viewStyle = { flex: 1 };
  const webViewConfig = {
    //startInLoadingState: true,
    //scalesPageToFit: true,
    javaScriptEnabled: true,
    //domStorageEnabled: true,
    //ignoreSslError: true,
    originWhitelist: ['*'],
    // allowFileAccess: true,
    // allowUniversalAccessFromFileURLs: true,
    style: { flex: 1 },
    source: (Platform.OS === 'android')
      ? { uri: 'file:///android_asset/view1/index.html' }
      : { uri: './assets/view1/index.html' }
  };
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView style={viewStyle}>
        <WebView {...webViewConfig} />
      </SafeAreaView>
    </>
  );
}