M1 (Apple Silicon) Macで既存のReact Nativeプロジェクトの開発環境が整うまで

公開:2020/11/26
更新:2020/12/01
7 min読了の目安(約6900字TECH技術記事 2

M1 Macで既存のReact Nativeプロジェクトを開発する環境が整うまでにしたことをまとめました。

Xcodeをインストール

Mac App StoreからXcode 12.2をインストールします。

Homebrewをインストール

公式サイトにあるインストールスクリプトを実行すると、以下のようにRosetta 2を使用するか、他のインストールオプションを勧められます。

Homebrew is not (yet) supported on ARM processors!
Rerun the Homebrew installer under Rosetta 2.
If you really know what you are doing and are prepared for a very broken
experience you can use another installation option for installing on ARM:
  https://docs.brew.sh/Installation

なるべくネイティブで動作させたいので、Installationのドキュメントを参照します。

However do yourself a favour and install to /usr/local on macOS Intel, /opt/homebrew on macOS ARM, and /home/linuxbrew/.linuxbrew on Linux. Some things may not build when installed elsewhere. One of the reasons Homebrew just works relative to the competition is because we recommend installing here. Pick another prefix at your peril!

macOS ARMでは/opt/homebrewにインストールするよう推奨されているので、/optに移動してhomebrewディレクトリを作成(ついでに所有権も変更)しておきます。

cd /opt
sudo mkdir homebrew
sudo chown <自分のユーザー名>:admin homebrew

HomebrewのGitHubからmasterを取得して展開します。

curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C homebrew

このままではパスが通っていないので、~/.zshrcにパスの設定を追加します。

.zshrc
export PATH=$PATH:/opt/homebrew/bin

Terminalを再起動するか、sourceコマンドで.zshrcの変更を反映します。

source ~/.zshrc

React Native開発環境をセットアップ

公式ドキュメントのReact Native CLI Quickstartの通り、nodeとwatchmanをHomebrewでインストールします。

brew install node
brew install watchman

プロジェクトの依存パッケージもインストールします。

npm install

iOSビルドを準備

cocoapodsをインストール

単純にcocoapodsをインストールしようとすると、cocoapodsが依存しているffiがエラーになります。

sudo gem install cocoapods
LoadError - dlopen(/Library/Ruby/Gems/2.6.0/gems/ffi-1.13.1/lib/ffi_c.bundle, 0x0009): missing compatible arch in /Library/Ruby/Gems/2.6.0/gems/ffi-1.13.1/lib/ffi_c.bundle - /Library/Ruby/Gems/2.6.0/gems/ffi-1.13.1/lib/ffi_c.bundle

現時点でffiをarm64でインストールするのは一筋縄ではいかなそうなので、ここは以下のissueで議論されている通り、Rosetta 2(64-bit intel)で動かすことで回避します。

このためにTerminalの設定を変えるのも何なので、arch -x86_64コマンドを使って64-bit intelアーキテクチャでcocoapodsをインストールします(ffiだけをx86_64でインストールした後、cocoapodsをarm64でインストールするとうまくいきませんでした)。

arch -x86_64 sudo gem install cocoapods

podsのインストールはarm64で実行できます。

12/1 追記

pod repo updateした後pod installするとまたffiのところでエラーになったので、x86_64で実行した方が無難かもしれません。

cd ios && arch -x86_64 pod install

PodsプロジェクトのBuild Settingsを設定

この段階でxcworkspaceを開いてSimulator向けのビルドをすると、エラーになります。

Undefined symbols for architecture x86_64:
...

これはPodsプロジェクトのBuilde SettingsでDebugビルド時のBuild Active Architecture OnlyONLY_ACTIVE_ARCH)をYesNoに変更することで回避できます。

このままだと再びpod installしたときにまたYesに戻ってしまうので、Podfilepost_installで上書きするように設定します。

Podfile
post_install do |installer|
  installer.pods_project.build_configurations.each do |build_configuration|
    build_configuration.build_settings['ONLY_ACTIVE_ARCH'] = 'No'
  end
end

12/1 追記

Podsプロジェクトだけでなく、プロダクトのプロジェクトの方もBuilde SettingsでDebugビルド時のBuild Active Architecture OnlyNoにしないとエラーになるケースがありました。🤔

11/27 追記

ほぼ同じ構成の別プロジェクトでSimulator向けのビルドを試したところ、以下のようなエラーになりました。

ld: in ..., building for iOS Simulator, but linking in object file built for iOS, file '...' for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

調べてみるとPods-ProductNameターゲットとPods-ProductNameTestsターゲットのBuild SettingsでExcluded ArchitecturesEXCLUDED_ARCHS)のAny iOS Simulator SDKarm64を指定していないことが原因でした。


(これはうまくビルドできた方のプロジェクト)

Podsプロジェクトのxcconfigで設定しているようですが、依存パッケージの違いか、エラーになった方のPodsプロジェクトではxcconfigにこの設定がなかったので、Podfilepost_installで設定するようにします。

Podfile
post_install do |installer|
  installer.aggregate_targets.each do |aggregate_target|
    aggregate_target.xcconfigs.each do |config_name, config_file|
      config_file.attributes['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] ='arm64'
      config_file.save_as(aggregate_target.xcconfig_path(config_name))
    end
  end
end

Bundle React Native code and images

今度はビルドフェーズのBundle React Native code and imagesでnodeが見つからないというエラーになります。

env: node: No such file or directory
Command PhaseScriptExecution failed with a nonzero exit code

ここで実行されるシェルスクリプトにnodeへのパスが通っていないだけなので、スクリプト内でパスを設定してもいいですが、CI環境でのビルドに影響が出ないように/usr/local/binにsymlinkを作ることにします。

sudo ln -s `which node` /usr/local/bin

Androidビルドを準備

Android Studioをインストール

公式サイトからダウンロードしてインストールします。UniversalでないのでRosetta 2での動作になります。

コマンドラインから使うことも考慮して、バンドルされているJDKにパスを通しておきます。

.zshrc
export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jre/jdk/Contents/Home

Emulatorを設定

M1はVT-xをサポートしていないので、Virtual Deviceを作成するときに警告が出ています。

無理矢理作成して起動してもエラーになります。

emulator: ERROR: x86 emulation currently requires hardware acceleration!
CPU acceleration status: Android Emulator requires an Intel processor with VT-x and NX support.  (VT-x is not supported)

トラブルシュートを見ると、物理デバイスを使うか、Intel Macで開発するか、ARMシステムイメージのVirtual Deviceを使う(ただし10倍遅い)か、と書かれています。

Troubleshoot
Unfortunately, your computer does not support hardware accelerated virtualization.
Here are some of your options:

  1. Use a physical device for testing
  2. Develop on a Windows/OSX computer with an Intel processor that supports VT-x and NX
  3. Develop on a Linux computer that supports VT-x or SVM
  4. Use an Android Virtual Device based on an ARM system image
    (This is 10x slower than hardware accelerated virtualization)

Other Imagesのところを見てみると、一応ARMシステムイメージのものがありますが、一番新しいのでもAndroid 7.1.1と少々古いです。

これで作成したVirtual Deviceを起動してみましたが、10倍どころではない遅さでホーム画面に辿り着くのに10分以上かかった上、最終的にはクラッシュしました。

これでは使い物にならないので、デバッグは物理デバイスを使うことにします。普段iOSで大体デバッグしてからAndroidで最終調整しているので、あまり問題にならないでしょう。

fastlane

fastlaneの実行でもやはりffiのインストールでエラーになります。

Undefined symbols for architecture arm64:
  "_ffi_call", referenced from:
      _ffi_raw_call in raw_api.o
      _ffi_java_raw_call in java_raw_api.o
...

An error occurred while installing ffi (1.13.1), and Bundler cannot continue.

こちらもx86_64で実行して回避します。

arch -x86_64 bundle install
arch -x86_64 bundle exec fastlane

まとめ

ここまでやって、ようやくiOSはSimulator、Androidは実機で動かしてデバッグができ、fastlaneによるビルドも回すことができるようになりました。
すでに対応も進んでいるようですが、ffiとAndroid Studioがarm64に対応したらすんなり開発環境を構築できそうです。