👷

Flutter の CocoaPods を rbenv と bundler で管理する

5 min read

背景

Flutter は開発環境を構築する際に CocoaPods のインストールを求められます。CocoaPods は iOS 側のライブラリ(依存)管理ツールで Flutter プラグインを使用してアプリを作成する上で必要になるため、Flutter のインストール時には CocoaPods のインストールが要求されます。

ただ、CocoaPods をグローバル環境に直接インストールしたものを使用するとあとで Ruby のバージョンが変わった際に CocoaPods の挙動にも影響をあたえ、CocoaPods が使えずビルドができないという現象が発生するかもしれません。また追加で fastlane などを使おうとするとこちらも Ruby のバージョンを考慮する必要があります。さらに Ruby のバージョンに対応した CocoaPods のバージョン、fastlane のバージョンも考慮する必要が出てきます。

Flutter で使用される CocoaPods はインストール手順から見てもグローバルにインストールされたものを使用しているらしく、Flutter doctor で CocoaPods がインストール済みかどうかはグローバルにインストールされているかをチェックしている感じです。(ここは詳しく調べていないので推測ですが、Flutter SDK の cocoapods.dart を見ると which pod でインストールされているか判断しているようです。)

目的

Flutter でグローバルの CocoaPods を使用しているとやはり上記のバージョン問題につながります。そこで iOS アプリの開発環境構築でよく目にする rbenv と bundler を使用して Ruby のバージョン管理とこれら Ruby 製ツールのバージョン管理を Flutter でのアプリ開発でも使用できるよう設定します。

開発環境

  • macOS Catalina 10.15.4
  • Flutter 2.2.1
  • Xcode 12.4
  • Android Studio 4.1.3

必要なもの

  • Flutter
  • rbenv
  • bundler
  • CocoaPods

rbenv が Ruby のバージョン管理。Bundler が gem (Ruby ライブラリ/パッケージ)の管理。CocoaPods は iOS ライブラリ管理。

事前準備

  1. Flutter のインストール(ここでは省略します)
  2. rbenv のインストール(ここでは省略します)

手順

  1. Flutter Project を新規作成(既に作成済みであればスキップして下さい。)
  2. Flutter Project 直下で以下を実行し、Project 内で Ruby のバージョンを固定します。
rbenv local {rbenv でインストール済みの Ruby Version を指定}
  1. 以下で bundler をインストールします。
gem install bundler
  1. Flutter Project 内の ios フォルダへ移動し、以下で Gemfile を作成します。
bundle init
  1. 作成された Gemfile に CocoaPods を追記します。
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

gem 'cocoapods' 
  1. 以下コマンドで CocoaPods のインストール
# ローカル環境(≒Flutterプロジェクト内)で gem をインストールする先を指定するよう設定
bundle config --local path vendor/bundle
# Gemfile に記載された gem をインストール
bundle install

# 以下はオプション
# ./vendor/cache へ gem のロックとキャッシュを行う
bundle package
# rubygems.orgへの接続なしで作成したキャッシュ(./vendor/cache)から gem をインストールする場合
bundle install --local
  1. 次に Flutter ビルド時に使用する CocoaPods を bundler でインストールした CocoaPods を使用するよう切り替えるために ${FlutterSDKインストール先}/flutter/packages/flutter_tools/lib/src/macos/cocoapods.dart にある以下箇所で CocoaPods を実行している箇所を bundler 経由で CocoaPods を実行するようを変更します。

before

Future<void> _runPodInstall(
      XcodeBasedProject xcodeProject, BuildMode buildMode) async {
    final Status status =
        _logger.startProgress('Running pod install...');
    final ProcessResult result = await _processManager.run(
      <String>['pod', 'install', '--verbose'],
      workingDirectory: _fileSystem.path.dirname(xcodeProject.podfile.path),
      environment: <String, String>{
        // See https://github.com/flutter/flutter/issues/10873.
        // CocoaPods analytics adds a lot of latency.
        'COCOAPODS_DISABLE_STATS': 'true',
        'LANG': 'en_US.UTF-8',
      },
    );

after

Future<void> _runPodInstall(
      XcodeBasedProject xcodeProject, BuildMode buildMode) async {
    // (オプション)正しく変更できているか確認するためにログの文言変更
    final Status status =
        _logger.startProgress('Running bundle exec pod install...');
    final ProcessResult result = await _processManager.run(
      // <String>['pod', 'install', '--verbose'],
      <String>['bundle', 'exec', 'pod', 'install', '--verbose'],
      workingDirectory: _fileSystem.path.dirname(xcodeProject.podfile.path),
      environment: <String, String>{
        // See https://github.com/flutter/flutter/issues/10873.
        // CocoaPods analytics adds a lot of latency.
        'COCOAPODS_DISABLE_STATS': 'true',
        'LANG': 'en_US.UTF-8',
      },
    );
  1. 手順7で Flutter SDK を手動で変更したので変更を反映させるために ${FlutterSDKインストール先}/flutter/bin/cache/flutter_tools.snapshot を一度削除してから、再度ビルドします。
  2. ビルド時のログで手順7の時にログの文言を変更している場合は変更した文言が出力されれば成功です。

*手順7,8は一度行えば、新たに Flutter プロジェクトを作成した場合に再度行う必要はありません。ただし、Flutter SDK の書き換えなので他の Flutter プロジェクトにも影響があることに注意して下さい。

CocoaPods 補足

複数人の開発になると特に明示しない限りは Flutter SDK を書き換えることはなく、bundler 経由で CocoaPods を実行せず、直接 CocoaPods を実行してしまうというケースも出てくるかもしれません。Podfile の一番上に以下を追加することで bundler 経由以外の CocoaPods の使用をした場合は実行を停止することが可能です。

using_bundler = defined? Bundler
unless using_bundler
  puts "\nPlease re-run using:".red
  puts " $ bundle exec pod install\n\n"
  exit(1)
end

まとめ

Flutter SDK を書き換える必要がありますが、この方法であれば一応ローカルで指定したバージョンの CocoaPods を Flutter が使用してくれるようになります。デメリットとしては Flutter SDK の書き換えが必要があること。また Flutter での iOS ビルド時には CocoaPods だけでなく、bundler も必須になるということです。さらに Flutter SDK のアップデートにより変更があった場合は Flutter SDK 書き換えの前に戻ることも考えられます。これらデメリットも考慮した上で使用するかどうか判断された方が良いかと思います。

こちらの記事は参考にある Flutter の Issue で提案されていた方法を実際に試してみたものとなります。2021/7/15現在、公式での対応はまだされていないですが、flutter build コマンドに pod install をスキップするような対応の Pull Request (feature: add pod-install flag #84568)も出ているようなので、対応を待ってみても良いかもしません。

参考

https://github.com/flutter/flutter/issues/40135
https://qiita.com/kurun_pan/items/520d91b4f5da6f14345b#flutter-sdkのアーキテクチャ
https://tech-blog.sgr-ksmt.org/2018/10/24/195401/