chakokuのブログ(rev4)

日々のごった煮ブログです

Flutterでwebviewを使う->簡単にはいかない (M1アーキだから? ffiでエラー)-> brewで入れ直して解消

諸般の事情でwebviewの動きを理解する必要があり、Flutterでwebviewを使ってみる。
webviewが使えるのは、iOS版とAndroid版のみのようなので、それぞれのemulator上で動きを試す。
使ったソースは以下

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'WebView test',
      home: WebView(
	initialUrl: 'https://google.com',
      ),
    );
  }
}

(_muhiro氏のソースを流用((FlutterでWebViewを表示する)))

iOS版では以下のエラーが出て動かなかった。

    /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require':
    dlopen(/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.5/lib/ffi_c.bundle, 0x0009): tried:
    '/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.5/lib/ffi_c.bundle' (mach-o file, but is an incompatible architecture (have 'arm64', need 'x86_64'))
    - /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.5/lib/ffi_c.bundle (LoadError)

上記以外にも以下のエラーが出た。ライブラリーバージョンの不一致のような印象。。詳細わからず。

    /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load
    such file -- 2.6/ffi_c (LoadError)

    /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require':
    dlopen(/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.5/lib/ffi_c.bundle, 0x0009): tried:
    '/Library/Ruby/Gems/2.6.0/gems/ffi-1.15.5/lib/ffi_c.bundle' (mach-o file, but is an incompatible architecture (have 'arm64', need 'x86_64'))
    - /Library/Ruby/Gems/2.6.0/gems/ffi-1.15.5/lib/ffi_c.bundle (LoadError)

iOS版は調べるのに時間がかかりそうだったので、Android版を試してみる。
環境構築次第とは思うけど、自分の環境ではAndroidは以下2系統利用可能であった。

sdk gphone arm64 (mobile)           • emulator-5554                        • android-arm64  • Android 11 (API 30) (emulator)
sdk gphone64 arm64 (mobile)         • emulator-5556                        • android-arm64  • Android 12 (API 32) (emulator)

適当にAndroid版を選択したら起動時に Errorが出た

│ The plugin webview_flutter_android requires a higher Android SDK version.                                          
│ Fix this issue by adding the following to the file /Users/sumi/lang/flutter/test_webview/android/app/build.gradle: 
│ android {                                                                                                          
│   defaultConfig {                                                                                               
│     minSdkVersion 19                                                                                               
│   }                                                                                                                
│ }                                                                                                                  
│                                                                                                                    
│ Note that your app won't be available to users running Android SDKs below 19.                                      
│ Alternatively, try to find a version of this plugin that supports these lower versions of the Android SDK. 

上記はwebviewライブラリのDocumentにも書かれている様だが、android/app/build.gradle内で、minSdkVerion に19を指定

defaultConfig {
    *略*
	//minSdkVersion flutter.minSdkVersion           // comment to avoid error
	minSdkVersion 19
    *略*
}

Android12(API32)では以下のエラーが出た(ストレージが足りないとは。。仮想ハードなのに・・)

[INSTALL_FAILED_INSUFFICIENT_STORAGE]
Error launching application on sdk gphone64 arm64.

Android 11 (API 30)を選択するとこれは動作した。ご参考に画面は以下

iOS版ではライブラリロード時のエラーが出ていて、全く動かないので、もうちょっと調べる必要あり。

webviewを動かして分かった事

  • webviewで表示した画面は描画エンジンが動いているだけなので、エクスプローラの様なURLを入れるフォームが無い。だから・・URLを入力して別のサイトには移れない(画面内にリンクがあると、リンク先に飛んでいく)
  • スマフォアプリでよく見るwebview画面は遷移元への戻るボタンがあるのだが、あの戻るボタンは作りこみされている?(この件はもうちょっと調べる必要あり)

■ご参考URL
webview_flutter | Flutter Package

■追記
iOS版で動かない件は、入っているライブラリとアーキテクチャの不一致という記事があり、それによると、エラーを起こしているライブラリを入れなおしたとある。確かに、エラーメッセージには、(have 'arm64', need 'x86_64')とあり、x86_64が欲しいのに、arm64が入っていると怒っている。うーん。。手パッチで入れ替えて大丈夫だろうか。言語の問題というより環境の問題なんだろう。
CocoaPods on M1 (Apple Silicon) fails with ffi wrong architecture - Stack Overflow
M1 Pods Install Crash - App Development - CodeCrew Community

いろいろ備忘録日記様より
Flutterメモ-01 (Apple M1でiOSアプリのビルドに失敗する件)(CocoaPods, ffi, Apple M1) - いろいろ備忘録日記

$ sudo arch -x86_64 gem install ffi

Flutterでなく、そもそもSwiftでwebviewを使った時にエラーないのか??をまず見るべきか?

Qiitaには、M1アーキ上でarm64とx86_64のruby環境を共存させる方法の詳細な解説がある(感謝)。これをよく読めば、まずい手パッチを回避して理解した上で構築できるかも。
M1 Macで、arm64とx86_64のruby環境を共存させて、Flutterの環境構築 - Qiita
Don't Use the Mac System Ruby – Use This Instead
M1 Mac

■これまでの理解
M1 MacはアーキはArmだが、Rosettaによりx86アプリも動くようになっている。
アプリはArm版とx86版があるので、どっちを使っているか注意が必要
使いたいアーキテクチャを指定してarchコマンドを実行することで、実行環境を切り替えることができる。
分からない点
archコマンドを使う事でRosettaがOn/Offされる?

Mac に Rosetta をインストールする必要がある場合 - Apple サポート (日本)
Appleの解説によると、「Rosettaはバックグランドで動き、自動変換する」とある。だったらアーキ選択は不要では?
archコマンドは、Rosettaを使いますという宣言??(自動変換だけどあえて宣言してみる的な?)

macos - How am I running x86_64 programs without arch on Apple Silicon? - Ask Different
これが正解かどうかは分からないけど、、
Rosettaが入っていたら自動変換されるからarchコマンドは不要、archコマンドはuniversal binaryに対して
どっちを使うかを明示的に指定したい場合に使う」
という説明があり。これなら納得。。

Apple Developer Documentation
Apple Developer Documentation

自分の理解
archコマンドはuniversal binaryのアプリに対してどちらのアーキで実行するかを選択できる
archコマンドにより、アーキテクチャ問い合わせコマンドの応答も x86/armのいずれかを指定できる。だから。。アプリ内でアーキテクチャに基づき分岐している場合、archコマンドでアーキを指定することで意図した方に分岐させることができる。

/usr/bin/rubyuniversal binaryx86/armどちらでも動かせる。 rubyからffiを呼びされた際、ffiはarm版なのだが、x86を要求されており、アーキ不整合で怒られている。だから、ワークアラウンドとして、共用ディレクトリのffix86版で書き換えろという情報が出ている(と理解)
この方法は、共用のffiを書き換えてしまうのであまりよろしくない。rubyが走っている時、自分(ruby)はx86環境と思ってしまっているのが元凶らしい。だから、みんなの対策は、/usr/bin/でない別のフォルダにarm用のrubyを追加インストールしろということだろう(多分)

rubyx86と思うのは、rbconfig.rb内の設定によるものらしい(他人の記事より)
rbconfig.rbはあちこちのディレクトリに大量に置かれていて、flutterが走る時どのconfigを読み込んでいるのかは分からないが、、たとえば一例としては以下。x86_64と指定されている。これがarmでないと、他のライブラリを呼び出す時にx86を要求してしまうのだろう。

% grep cpu /System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/universal-darwin21/rbconfig.rb 
  CONFIG["target_cpu"] = "universal"
  CONFIG["host_cpu"] = "x86_64"
  CONFIG["build_cpu"] = "x86_64"

みなさんが提案しているworkaroundは混在するアーキをどちらかに統一できる環境を構築することなんだろうなと理解。基本Armなんだから、実行効率のよいArmだけで走る環境ができたらいいのではと。だったら、自分でx86と思い込んでいるrubyをArm環境で仕立てるということか。普通に走らせただけでも、rubyx86環境にいるつもりになっているので、、これをbrewとかでインストールしてArmと自覚するように仕向けると。。

% arch
arm64

% which ruby
/usr/bin/ruby

% ruby -e 'require "rbconfig" ; pp RbConfig::CONFIG' | grep host
 "host_os"=>"darwin21",
 "host_vendor"=>"apple",
 "host_cpu"=>"x86_64",
 "host"=>"x86_64-apple-darwin21",
 "host_alias"=>"",

と理解したつもりで、自分としてはArmとして動くrubyをローカルかどこかに入れてみる。
sudoなければ/usr/binなどには書かないだろうと期待して、brewコマンドでrubyを入れてみる。

% brew install -s ruby

    *略*

By default, binaries installed by gem will be placed into:
  /opt/homebrew/lib/ruby/gems/3.1.0/bin

You may want to add this to your PATH.

ruby is keg-only, which means it was not symlinked into /opt/homebrew,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.

If you need to have ruby first in your PATH, run:
  echo 'export PATH="/opt/homebrew/opt/ruby/bin:$PATH"' >> ~/.zshrc

For compilers to find ruby you may need to set:
  export LDFLAGS="-L/opt/homebrew/opt/ruby/lib"
  export CPPFLAGS="-I/opt/homebrew/opt/ruby/include"

For pkg-config to find ruby you may need to set:
  export PKG_CONFIG_PATH="/opt/homebrew/opt/ruby/lib/pkgconfig"

sudo を付けなかったが、/opt/homebrew配下のフォルダにインストールされた*1

% pwd
/opt/homebrew/Cellar/ruby/3.1.1/bin

% ls
bundle		gem		rake		rdoc		typeprof
bundler		irb		rbs		ri
erb		racc		rdbg		ruby

homebrew配下に入れたrubyは、自分はArmアーキだと思っているようだ。だから、PATH指定してこっちのrubyが優先して走るようにしたら、アーキ不整合が解消するのでは、、と期待

% ./ruby -e 'require "rbconfig" ; pp RbConfig::CONFIG' | grep host
 "host_os"=>"darwin21",
 "host_vendor"=>"apple",
 "host_cpu"=>"arm64",
 "host"=>"arm64-apple-darwin21",
 "host_alias"=>"",

zshのPATH変数にhomebrew配下のrubyを追加

% cat setup_ruby.sh 
export PATH="/opt/homebrew/opt/ruby/bin:$PATH"
export PATH="/opt/homebrew/lib/ruby/gems/3.1.0/bin:$PATH"

% source ./setup_ruby.sh 
% which ruby
/opt/homebrew/opt/ruby/bin/ruby

flutterを動かしたが同じエラーで止まる。そもそもは、cocoapodからエラーが出ているので、cocoapodも入れ直さないといけないのだろうか。
先人の記事でもhomebrew配下のbrewでcocoapodをインストールしているので、それに倣ってやってみる。

% ruby -v
ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [arm64-darwin21]
% which gem
/opt/homebrew/opt/ruby/bin/gem
% gem install cocoapods

homebrew配下のgemでcocoapodsをインストールすると、/opt/homebrew/lib/ruby/gems/3.1.0/gems/ の下に
cocoapodsのライブラリ?が入る様だった。
この状態で再度走らせると、エラーなく動作して、iOS Simulator上でwebviewが走った。先人に感謝。。
忘れないうちに整理するとやったことは以下

  • brewを使って/ope/homebrew 配下にrubyをインストール
  • homebrew配下のrubyとgemが優先されるようにPATHを設定
  • homebrew配下のgemを使ってcocoapodをインストール

上記操作で作った環境だとFlutter+cocoapodが正常に動作した。

Android版でinsufficient stroage (確か)のエラーがまだ残っているが、やっかいそうなエラーが解消できたのでよかった。解法を書いてくださった先人の方々に感謝でございます。

■追記
Android Simulatorでstorageのエラーが出るのは既知のようで、ADV(Android Virtual Device)の設定で解消するらしい

*1:/opt/homebrewのownerは自分になっていた。ちょっと驚き