Yuri’s Tech Note

技術系アウトプット

XcodeのReact NativeにおけるBuild Phases

f:id:yuri_iOS:20180922175648p:plain

XcodeのArchiveビルドが私だけできないという現象に遭遇し、原因がBuild Phasesにあったのだがその際にBuild Phasesの処理を概要だけでも知っておこうとまとめた。

React NativeアプリのBuild Phasesは下図のようになっていて全部で10 項目ある。
f:id:yuri_iOS:20180922161157p:plain

各項目でどんな処理が行われているか順を追って見て行く。

Target Dependencies

アプリで使用されるフレームワークはそれを使用するアプリよりも先にビルドされるよ必要があり、これを指してdependency(依存関係)と呼ぶ。
下図のように自作したライブラリのプロジェクトを使用するような場合に、どちらを先にビルドするかXcodeに教えてあげる際に使用される。
f:id:yuri_iOS:20180922143740p:plain
xcode target dependencies between two projects - Stack Overflow

[CP]Check Pods Manifest.lock

Pods Manifest.lockはPodfile.lockのコピーであり、pod installをする度に生成される。
Pods Manifest.lockとPodfile.lockに差分があるとエラーが出力される。

diff "${PODS_PODFILE_DIR_PATH}/Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
if [ $? != 0 ] ; then
    # print error to STDERR
    echo "error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." >&2
    exit 1
fi
# This output is used by Xcode 'outputs' to avoid re-running this script phase.
echo "SUCCESS" > "${SCRIPT_OUTPUT_FILE_0}"

Podsディレクトリは常にバージョン管理下にあるわけではないので、開発者が実行前にポッドを更新するように教えるための仕組み。

Bundle React Native Code And Images(1つめ)

React NativeのバグなのかBundle React Native Code And Imagesは2つ存在し、しかも1つ目では手前のCheck Pods Manifest.lockで行っているのと同様にPods Manifest.lockとPodfile.lockの差分を確認している。
f:id:yuri_iOS:20180922145627p:plain

Compile Sources

ライブラリはリンクの仕方によって,Static LibraryとDynamic Libraryに分類できる。
Static Libraryはアプリケーションのコンパイル時に組み込まれる形で(静的に)リンクし、Dynamic Libraryはアプリケーションの実行時にローダがライブラリを検索し,(動的に)リンクするという違いがある。

ここではコンパイルする静的ライブラリの中でも実行ファイルであるAppDelegate.mとmain.mが指定されている。

Link Binary With Libraries

ここも静的ライブラリのコンパイルするものが並んでいるがライブラリの静的ファイルを扱っている。

Copy Bundle Resources

フォントや画像など、bundleしたアプリで使用する素材たちが列挙されている。

Bundle React Native Code And Images(2つめ)

ここではnode_modules/react-native/scripts/react-native-xcode.shを実行し、その中でjsのコードと手前でコピーした画像をbundleする処理を行っている。

react-native-xcode.sh

コード
react-native/react-native-xcode.sh at master · facebook/react-native · GitHub

今回私の環境でArchiveビルドができなかった原因としてnvm lsコマンドでインストールされているバージョンを見ることができるがここのdefaultが設定されていなかった。
(default設定後)
f:id:yuri_iOS:20180922144056p:plain

react-native-xcode.shの中でnodeを設定しているがここら辺で問題が起きた様子。
ちなみにシミュレーターだとビルドできていたのは下記のコードによるもの。

if [[ "$PLATFORM_NAME" == *simulator ]]; then
      if [[ "$FORCE_BUNDLING" ]]; then
        echo "FORCE_BUNDLING enabled; continuing to bundle."
      else
        echo "Skipping bundling in Debug for the Simulator (since the packager bundles for you). Use the FORCE_BUNDLING flag to change this behavior."
        exit 0;
      fi
    else

run script

ここでもreact-native-xcode.shを実行している。
同じ処理をしてしまっている項目については削除しても問題ないかもしれないしビルド時間が短縮されそうなので今後上司に聞いてみたい。

[CP]Copy Pods Resources

Pods-AppName-resources.shを実行
bundleしたライブラリたちをインストールした後にシンボリックリンクをコピー

if [[ "$CONFIGURATION" == "Debug" ]]; then
  install_resource "${PODS_ROOT}/FBSDKCoreKit/FacebookSDKStrings.bundle"
  ・・・
fi

mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
  mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
  rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
fi
rm -f "$RESOURCES_TO_COPY"

[CP]Embed Pods Frameworks

Pods-AppName-frameworks.shを実行
おおよそ上と同じことをframeworkを対象にしている。

まとめ

1.ライブラリを使用する前にその管理ファイルであるPods Manifest.lockを確認
2.アプリの大本となるAppDelegate.mやmain.mをコンパイルしてバイナリファイルを生成
3.使用するライブラリのバイナリファイルを紐づける
4.アプリで使用する素材系のbundleされたデータをコピー
5.JavaScriptのコードをbundle
JSはスクリプト言語なのでコンパイル不要bundleのみ
6.bundleされたライブラリをコピー
7.フレームワークを埋め込む

コンパイルする必要があるものをコンパイル
その後で既に提供されているであろうライブラリのバイナリファイルを紐づけ、
その後に素材やJSなどのコードをbundle

ライブラリのコンパイルやbundleをしているところが見当たらないのでライブラリの方で既に作成して提供してくれているのかもしれない。