Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

Studyplus iOSでライブラリ管理をCocoaPodsからCarthageに移行した件

こんにちは。今年の5月に入社したiOSエンジニアの大石(id:k_oishi)です。 今回は弊社がリリースしているStudyplusのiOSアプリのライブラリ管理をCocoaPodsからCarthageに移行した件をご報告します。

プロジェクトの構成と開発PCのスペック

現在のStudyplus iOSは以下の構成となっています。

  • 開発環境: Xcode 10.3
  • 言語: Swift, Objective-C(割合は86:14)
  • ライブラリ管理: CocoaPods, Carthage
  • CI/CD: fastlane, CircleCI, DeployGate

また、私が入社時に希望して用意されたMacBook Proのスペックは以下のとおりです。 f:id:k_oishi:20190913153002p:plain

気になるライブラリのビルド時間

私が入社した時点でCarthageの導入はされていましたが、Carthageで管理されていたライブラリは一部のみでした。 ほぼ全てのライブラリがCocoaPodsで管理されている状態でした。

そのような状態で開発を進めていましたが、クリーンビルドで5分以上の時間がかかり、ちょっとしたコードの修正やブランチの切り替えの際にライブラリのビルドが走るのは結構つらいものがありました。

f:id:k_oishi:20190913153121p:plain

そこで私は過去に経験したプロジェクトでもCarthageへの移行の経験がありましたので、チームメンバーと相談してCarthage移行を進めることになりました。

Carthage移行のはじまり

まず、現在CocoaPodsで管理しているライブラリからCarthageに対応しているライブラリを洗い出しました。 以下のライブラリが対応していましたので、1つずつ順次進めて行きました。

  • Apollo
  • Facebook SDK
  • Firebase
  • Nuke
  • Realm
  • SVProgressHUD

    特定のライブラリのビルドがうまくいかない件

    Carthage移行を進めていく中でビルドエラーに遭遇しました。 f:id:k_oishi:20190913153251p:plain

このエラーは以下のライブラリの移行中に発生しました。

  • Facebook SDK
  • Firebase
  • Realm

Argument list too long(コマンドの引数が長すぎる)というエラーのようですが、ライブラリのビルドについてあまり詳しくない人にとって、なぜこれが起きたのか全くわかりません。ただ、なんとなくですが、同じような引数のパスがひたすらループしているように見えました。(このスクリーンショットは一部分で全体はこの数十倍の量)

プロジェクトの設定の変更

f:id:k_oishi:20190913153333p:plain

プロジェクトの設定をひたすら見直していたところ、FRAMEWORK_SEARCH_PATHS$(SRCROOT)recursiveになっているのがビルドエラーの原因かもしれないことがわかりました。まず既存のコードでnon-recursiveに変更してビルドが通るかを確認してから、Carthageの移行を行いビルドできることを確認しました。

(補足) CarthageでのFirebase導入の注意点

Firebaseの各ライブラリをCarthageから導入する場合は以下のようなJSONファイルをCartfileに指定し、バイナリ形式でダウンロードされる仕組みです。

binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" == 6.7.0
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDynamicLinksBinary.json" == 6.7.0
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAdMobBinary.json" == 6.7.0
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" == 6.7.0

なお、Firebaseの各frameworkはStatic Libraryのため、従来のライブラリに必要なcopy-frameworksの手順は必要ありません。 また、導入するFirebaseの機能によっては追加で関連するライブラリ等をLink Binary With Librariesに追加する必要があります。

詳細はこちらのエントリを参考にしてください。 Crashlytics をどうしても Carthage で使いたいあなたへ

(補足) LicensePlistの設定変更

アプリで使用しているライブラリのライセンス情報の表示にLicensePlistを使用している場合、CocoaPods使用時に取得できていたライセンス情報がCarthage移行後に取得出来なくなる場合があります。 今回のケースではJSONファイルに記述されたバイナリを取得しているFirebaseが該当します。

このような場合、Firebaseのライセンス情報を表示するためにlicense_plist.ymlを作成して以下のような記述を追加しました。これでこれまでと同様にライセンス情報が表示されるようになりました。

github:
  - owner: firebase
    name: firebase-ios-sdk
    version: 6.70.0

rename:
  firebase-ios-sdk: Firebase

以上でローカルの開発PC上のCarthageへの移行が完了しました。

Carthage対応のまとめ

結果、ローカルの開発PCでは以下のようにビルド時間を短縮することができました。 移行前 5分〜6分移行後 1分40秒〜2分

もちろん初回は$ carthage bootstrap~の実行が必要ですので、その分のビルド時間はかかるのですが、開発の最中にライブラリの再ビルドが走ることはなくなり快適に開発を行えるようになりました。

今回の移行では以下がポイントとなりました。

  • 1ずつ移行を試してビルドエラーの原因を特定しやすくする
  • ビルドエラーが出る場合はプロジェクトの設定を見直す
  • Firebaseの導入は他と少し違うので要チェック
  • ライブラリによってはLicensePlistの対応が必要になるかも

CIの改善

CircleCIでのCarthageのキャッシュ利用

ローカルの開発PCでの移行が完了しましたので、CircleCIの設定を変更してCarthageのビルド結果をキャッシュするようにします。 キャッシュの設定をしないと毎回Carthageのビルドが走り、ローカルの開発PCと比べるとCI環境ではとんでもないビルド時間が毎回かかってしまいます。

f:id:k_oishi:20190913153428p:plain

キャッシュを利用するためにはconfig.ymlに以下のステップを追加します。

  setup_carthage:
    steps:
      - restore_cache:
          key: v1-ca-{{ checksum "Cartfile.resolved" }}
      - run:
          name: Carthage
          command: carthage bootstrap --platform iOS --cache-builds --no-use-binaries
      - save_cache:
          key: v1-ca-{{ checksum "Cartfile.resolved" }}
          paths:
              - Carthage

キャッシュを利用することで初回のみはそれなりの時間がかかりますが、ライブラリに変更がない場合は以下のように数十秒でCarthageの設定を終わらせることが出来るようになりました。

f:id:k_oishi:20190913153455p:plain

さらなるCIの高速化を求めて…〜CocoaPods〜

Carthage導入後のCIの結果を眺めていたところ、もう少しビルド時間を改善できそうな部分を見つけました。CocoaPodsのステップです。

f:id:k_oishi:20190913153659p:plain

このステップで時間を要していたのがCircleCIが独自に用意したコマンドです。これはCocoaPodsのリポジトリの取得を従来より早く取得できるというものでした。 curl https://cocoapods-specs.circleci.com/fetch-cocoapods-repo-from-s3.sh | bash -s cf

しかし、CocoaPodsは少し前にリリースされたv1.7からCDNに対応しました。これによりリポジトリを取得する必要がなくライブラリを導入できるようになりました。要するに前述のコマンドを実行する必要が無くなったのです。このコマンドの実行を削除した結果、以下のように当該ステップの時間を短縮することができました。

f:id:k_oishi:20190913153739p:plain

CI高速化の結果

Carthage移行前は毎回ビルドに20分近くかかっていましたが、Carthage移行後のキャッシュが効いた状態では8分程度でビルドが終わるようになりました。

Carthage移行前 f:id:k_oishi:20190913153811p:plain

Carthage移行後 f:id:k_oishi:20190913153839p:plain

いかがでしたか?

今回のCarthageへの移行を行ったことで、開発時とCIのビルド時間を短縮することができました。

ビルドエラーが発生すると解決するのがやや大変ではありますが、ライブラリのより深い世界を知るきっかけにもなるかと思います。 現在もCocoaPodsでライブラリ管理されているプロジェクトもあるかと思いますが、ぜひ試されてはいかがでしょうか。

先日のiOSDC Japan 2019の以下のセッションではCarthageやCocoaPodsで使用されるライブラリやフレームワークに関する解説やビルドエラーの際の原因の切り分け方法がわかりやすくまとまっており、大変参考になる内容でした。

また、今後ライブラリ管理の選択肢の1つになりそうなSwift Package Manager(SwiftPM)の動向もチェックしたいと思います。