22時に寝ようと思って2時に寝る。

備忘録や日記を書いてます。きょうは早く寝よう。

CircleCI - iOSシミュレーター向け(x86_64)にアーカイブをビルドする

はじめに

CircleCI 上で、x86_64 アーキテクチャで動作する iOS シミュレーター向けにアーカイブをビルドする方法について紹介します。結論としては、fastlane の xcodebuild コマンド を用いて、SDK のパラメータに Simulator用 SDK を指定してあげることで実現できます。

背景

今回、手元に iPhone の実機が手元にない開発メンバーでも動作確認できるように iOS Simulator で動くアーカイブ.app ファイル)が必要になった、という経緯がありました。

以前から、CI上で App Store へアップロードするための実機向けのアーカイブ.ipaファイル)をビルドして Artifacts に置いて共有する運用としており、この .ipa ファイルを QA メンバーが参照して、そのビルド時点での動作を検証するなどしていました。

ただ、ここで共有されるアーカイブは実機の iPhone で動作する arm64 アーキテクチャ向けにビルドされたものであり、x86_64 アーキテクチャで動いている iOS Simulator では動作しません。そんな訳で、今回新しく iOS Simulator でも動作するアーカイブを生成し、共有する必要が出てきました。

iOS のデバイスごとのアーキテクチャの違いについてはこちらの記事が詳しいですので、ご参照下さい。

Fastfile に iOS Simulator をアーカイブする lane を追加する

以下の lane を追加しました。中身はシンプルで、xcodebuild コマンド を呼び出しているだけです。配信する予定のないアーカイブのため、署名作業は省いています。

lane :archive_for_simulator do
  xcodebuild(
    scheme: "MyApp",
    workspace: "MyApp.xcworkspace",
    configuration: "Release",
    sdk: "iphonesimulator",
    derivedDataPath: "build"
  )
end

指定しているパラメータ

パラメータ名 説明
configuration 構築する際に使用する Build Configuration を指定する。今回は、リリース時と同様の動作を見たいので、"Release"を指定。
sdk プロジェクトの構築する際に使用するSDKの名前またはパスを指定する。今回は、"iphonesimulator"を指定。
derivedDataPath 成果物を配置するパスを指定する。今回は、"build"ディレクトリを指定。

使用できる SDK の一覧を取得する

sdk パラメータで指定できるSDK は、xcodebuild -showsdks で調べることができます。 iphonesimulator13.4 というようにバージョン名も併せて指定することもできますが、このバージョンは CircleCI で指定している macOS イメージに依存しているため、あえて指定しないことで、自動的に追従できるようにしておくと良いと思います。

$ xcodebuild -showsdks
iOS SDKs:
    iOS 13.4                          -sdk iphoneos13.4

iOS Simulator SDKs:
    Simulator - iOS 13.4              -sdk iphonesimulator13.4

macOS SDKs:
    DriverKit 19.0                    -sdk driverkit.macosx19.0
    macOS 10.15                       -sdk macosx10.15

tvOS SDKs:
    tvOS 13.4                         -sdk appletvos13.4

tvOS Simulator SDKs:
    Simulator - tvOS 13.4             -sdk appletvsimulator13.4

watchOS SDKs:
    watchOS 6.2                       -sdk watchos6.2

watchOS Simulator SDKs:
    Simulator - watchOS 6.2           -sdk watchsimulator6.2

成果物が配置されるパス

上記の例だと、derivedDataPath にはbuild ディレクトリを指定していますが、この場合は build/Build/Products/Release-iphonesimulator/ 配下に MyApp.app が配置されるようになります。

gym コマンドを使っていない理由

余談ですが、もともと xcodebuild コマンドではなく、 gym コマンド を用いてビルドしようとしていましたが、パッケージングの途中に発生するエラーが回避できなかったため、代替手段として xcodebuild コマンドを使用しています。

.circle/config.ymliOS Simulator 向けのアーカイブを生成するジョブを追加する

最終的には以下のような設定としました。

version: 2.1

executors:
  default:
    macos:
      xcode: "11.3.0"
    shell: /bin/bash --login -eo pipefail
    environment:
      FL_OUTPUT_DIR: /Users/distiller/project/output

jobs:
  archive_for_simulator:
    executor: default
    steps:
      - run: mkdir $FL_OUTPUT_DIR
      - checkout
      - setup_ruby_gems
      - setup_cocoapods
      - run:
          name: Archive for simulator
          command: bundle exec fastlane archive_for_simulator
      - run:
          name: Compress the archive
          command: |
            cd build/Build/Products/Release-iphonesimulator/
            zip -r MyApp.app.zip MyApp.app
            mv MyApp.app.zip $FL_OUTPUT_DIR/
      - store_artifacts:
          path: /Users/distiller/project/output

ジョブの最後で、生成されたアーカイブを Artifacts にストアするようにしています。

f:id:azuuun:20200427154917p:plain
Artifacts タブからストアしたアーカイブを確認できる

このジョブをリリースする際の Workflow から呼び出してあげることで AppStore にアップロードされるアーカイブ相当、かつ、iOS Simulator で実行・確認できるアーカイブを生成・共有できます。

さいごに

昨今の情勢では、開発チーム全員が在宅勤務のため開発に使用できる実機デバイスが手元になく、QAメンバーが試験できないというケースも少なくないと思います。その際、暫定対策的に iOS Simulator を用いて試験を実施する場合は、この記事のような方法が応用できると思います。

参考