Build static frameworks to speed up app’s launch times

iOSのライブラリ管理ツールCarthageの動的フレームワークを静的フレームワークにしてアプリ起動時間を短くした話

話すこと

本記事は、下記のCarthageのDocumentsにある Build static frameworks to speed up app’s launch timesを実際に試してつまづいた点について話します。

github.com

先駆者がいらっしゃるので、そちらの記事もぜひ参考にしてください。

medium.com

ざっくりした手順

  1. carthage update --platform ios --no-use-binaries
  2. ld.pyを設置
  3. carthage-build-static.shを作成
  4. chmod +x ~
  5. 静的フレームワークの作成

1. carthage update --platform ios --no-use-binaries

Documentsにも書いてある通り、--no-use-binariesオプションを付けてcarthage updateする。 もともとあるバイナリデータを用いていると、依存?があったりして上手く静的フレームワークを作成できないらしい。

carthage update --platform ios --no-use-binaries

2. ld.pyを設置

Xcode 9以降(New Build System)を使っている方は必要ありません

github.com

下記からld.pyをDownloadし、Project内の好きな場所に置きます。

swift-staticlibs/ld.py at master · keith/swift-staticlibs · GitHub

3. carthage-build-static.shを作成

Xcode 9のNew Build Systemを使っているかで違います

New Build Systemを使っている方

#!/bin/sh -e

xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
trap 'rm -f "$xcconfig"' INT TERM HUP EXIT

echo "MACH_O_TYPE = staticlib" >> $xcconfig
echo "DEBUG_INFORMATION_FORMAT = dwarf" >> $xcconfig

export XCODE_XCCONFIG_FILE="$xcconfig"

carthage build "$@"

上記をcarthage-build-static.shとして保存します。

issueに書いてあったものを参考にさせていただきました。

github.com

New Build Systemを使っていない方

#!/bin/sh -e

xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
trap 'rm -f "$xcconfig"' INT TERM HUP EXIT

echo "LD = $PWD/the/path/to/ld.py" >> $xcconfig
echo "DEBUG_INFORMATION_FORMAT = dwarf" >> $xcconfig

export XCODE_XCCONFIG_FILE="$xcconfig"

carthage build "$@"

上記をcarthage-build-static.shとして保存します。

echo "LD = $PWD/the/path/to/ld.py" >> $xcconfig

その時に、PATHをld.pyを置いた場所に変更してください。 直下に置いた際は、$PWD/ld.pyになります。

4. chmod +x ~

実行権限を与えるため、コマンドを実行します。

chmod +x carthage-build-static.sh
# ↓Xcode 8以前を使っている方は不要
chmod +x ld.py

5. 静的フレームワークの作成

まず、正しく静的フレームワークが作成できているか、1つのライブラリで確認します。

自分の場合はSnapKitで試しました。

./carhage-build-static <Library名> --platform ios

実行後、Carthage/Build/iOS/Static/~にFrameworkが生成されていれば成功です。(CarthageのDocumentにはStaticディレクトリ下に生成されると書いてありませんでした...)

静的なものかどうかは、

file Carthage/Build/iOS/Static/<Library名>.framework/<Library名>

このコマンドを打って下の様にcurrent ar archiveかどうかで確認できます。

確認できたら、CarthageでCarthage/Build/iOS/Static/~にあるライブラリを追加しましょう。

carthage copy-frameworksにあるInput Filesに動的フレームワークのPATHを登録している場合は、消しましょう。

更に、もともと作成していたCarthage/Build/iOS/~にある動的フレームワークは削除しましょう。

削除しないと、動的のものを参照してしまいimage not foundのエラーが出てしまう可能性があります。

エラーが出ずに実行できたら、全てのライブラリに適用しましょう。

./carhage-build-static --platform ios

これを実行すれば、Carthageに登録しているライブラリ全ての静的フレームワークが作成されます。

あとは同様にアプリに登録するだけです。

注意する点

イメージリソースを使用しているライブラリは、静的フレームワークを使用できません。

具体的にはSVProgressHUDがそうでした。作成は成功するのですが、Build時にエラーが出ます。

linkerエラーなどが出た際は、それが発生しているライブラリを動的なものに戻しましょう。

静的フレームワークが作成されない場合

github.com

このissueを参考にしてみてください。それでも作成できない場合はあきらめましょう...

結果

環境

Xcode - Version 10.0

Simulator - iPhoneXs MAX iOS 12.0

Time

Before(Dynamic) After(Static)
total pre-main time 882.84 milliseconds (100.0%) 794.82 milliseconds (100.0%)
dylib loading time 270.73 milliseconds (30.6%) 233.54 milliseconds (29.3%)
ebase/binding time 248.72 milliseconds (28.1%) 225.53 milliseconds (28.3%)
objC setup time 82.90 milliseconds (9.3%) 77.49 milliseconds (9.7%)
initializer time 280.31 milliseconds (31.7%) 258.05 milliseconds (32.4%)

動的フレームワークを静的にしたので、表のdylib loading timeが大幅に減少するはずなのですが、

270msec -> 233msecとあまり大きな結果は得られませんでした。

大きめのアプリでもやってみましたが、参考の記事よりも大幅に減少する結果にはなりませんでした。

全てのライブラリを静的にできてないからでしょうか、それともNew Build Systemが上手くやっているおかげなのでしょうか...

Before After
total pre-main time 2.7 seconds (100.0%) 2.3 seconds (100.0%)
dylib loading time 706.75 milliseconds (25.6%) 492.44 milliseconds (20.6%)
ebase/binding time 1.2 seconds (44.2%) 1.1 seconds (46.4%)
objC setup time 164.16 milliseconds (5.9%) 218.17 milliseconds (9.1%)
initializer time 666.76 milliseconds (24.1%) 565.18 milliseconds (23.7%)

終わりに

期待していたよりあまり効果がでず、うーん..という感じです。

CIなどを行なっている場合は、変更する努力に見合う成果が得られるか検討する必要がありそうですね。

参考

CarthageでStatic FrameworkとしてビルドしてiOSアプリの起動時間を短縮する – Ryoichi Izumita – Medium

Static frameworks - Task failed with exit code 65 · Issue #2425 · Carthage/Carthage · GitHub

Automatically change dynamic frameworks to static? · Issue #2575 · Carthage/Carthage · GitHub

iOSアプリ起動時のダイナミックリンクライブラリの読み込み時間を調べる