コドモン Product Team Blog

株式会社コドモンの開発チームで運営しているブログです。エンジニアやPdMメンバーが、プロダクトや技術やチームについて発信します!

Cloud SigningでiOSアプリ開発の面倒な作業から解放された話🎉

はじめに

こちらは「コドモン Adevent Calendar 2024」の6日目の記事です。

qiita.com

こんにちは、エンジニアの重田です。2024年はモバイルアプリの基盤をCapacitorに移行しました。ビルド周りにも手を入れてiOSアプリのCloud Signingを導入したところ、地味に面倒な作業から解放されました!

🎉年に1回の証明書やProvisioning Profileの更新作業がゼロに🎉

🎉検証用のデバイスを追加するたびにProvisioning Profileを更新する作業がゼロに🎉

🎉CIマシンにProvisioning Profileなど必要なファイルを設置する作業がゼロに🎉

前提

  • Xcode Cloudは使っていません
  • Xcodeで、Automatically manage signingのチェックをいれる必要があります
  • Github ActionでiOSアプリをビルドし、AppStoreConnectにアップロードしています
  • 今回ビルドしているアプリは、純粋なネイティブアプリではなく、Capacitorを利用しています

本記事で紹介する内容は、純粋なネイティブアプリでも、クロスプラットフォーム技術(Flutter, ReactNative, Capacitorなど)を使ったアプリでも同じように使えると思います。

Cloud Signingとは?

これまでローカルやCIマシンで行っていたiOSアプリのコード署名を、クラウド(Appleのサーバー)で行う技術です。

基本的な処理の流れ

  1. xcodebuild archiveでアーカイブを作成
    • このとき通常はコード署名を行うが、Cloud Signingの場合はスキップする
  2. xcodebuild -exportArchiveでipaファイルをエクスポート
    • このときクラウド上で署名する
  3. AppStoreConnectにアップロード

Cloud Signingを利用する場合、証明書や秘密鍵をCIマシンに配置する必要はありません。なので、年に1回の証明書の更新作業や、デバイスを追加するたびにprovisioning profileを更新する作業から開放されます!

処理の詳細

1. Archive

- name: archive
  run: |
    xcodebuild archive \
      -workspace ios/App/App.xcworkspace \
      -scheme App \
      -sdk iphoneos \
      -configuration $BUILD_CONFIGURATION \
      -archivePath App.xcarchive \
      CODE_SIGNING_REQUIRED=NO \
      CODE_SIGNING_ALLOWED=NO
  shell: bash

※workspace, schemeなどの値は、プロジェクトにあわせて書き換えてください ※$BUILD_CONFIGURATIONはビルドの種類に応じてDebug/Releaseなどを設定してください

ここでのポイントはCODE_SIGNING_REQUIRED=NOCODE_SIGNING_ALLOWED=NO です。アーカイブ時にはコード署名をスキップすることで、エクスポート時にCloud Signingを利用できるようになります。

2. Entitlementsの指定

このステップは情報が少なく、一番苦労しました。

当初、この処理無しで進めていたところ、プッシュ通知が届かなくなりました。プッシュ通知を利用するためにはEntitlementsの設定が必要ですが、Entitlementsはコード署名に埋め込まれるので、前述のArchiveステップでコード署名をスキップしたことにより未設定の状態になっていたのです。

この問題は、次のようにEntitlementsを設定して解決しました。

- name: Specify entitlements
  run: |
    ENTITLEMENTS_PATH="ios/App/App/App.entitlements"
    codesign --entitlements $ENTITLEMENTS_PATH -f -s "-" \
      App.xcarchive/Products/Applications/App.app
  shell: bash

※Entitlementsやappのパスは、プロジェクトにあわせて書き換えてください

ここでのポイントはcodesignのオプションに指定している-s "-"です。

通常だと、-sオプションにはiPhone DistributionのようなSIGNING IDENTITYを指定する必要があります。そのためには証明書や秘密鍵も必要だったりして、せっかくのCloud Signingのメリットがなくなってしまいます。

そのとき、よくよくman codesign を見てみると"-"を指定するやり方を発見しました。ad-hoc signingとして処理され実質コード署名されないようですが、今回はEntitlementsを設定したいだけで、実際の署名はクラウドで行われるためこちらを利用しました。

3. Export

Exportについては、WWDCのこの動画がわかりやすいです。 https://developer.apple.com/jp/videos/play/wwdc2021/10204/

16:30秒あたりからの「Automating distribution」の部分です。

※動画の20:41のところから引用

アーカイブファイル、Exportの設定を記述したplist、AppStoreConnectのAPIキーの3つをつかって、xcodebuild -exportArchiveを実行します。

- name: export IPA
  id: export-ipa
  run: |
    xcodebuild \
      -exportArchive \
      -archivePath App.xcarchive \
      -exportOptionsPlist ios/App/App/ExportOptions/Debug.plist \
      -exportPath dist \
      -allowProvisioningUpdates \
      -authenticationKeyPath $AUTH_KEY_PATH \
      -authenticationKeyID ${{ inputs.apple-api-key-id }} \
      -authenticationKeyIssuerID ${{ inputs.apple-api-issuer-id }}
  shell: bash

※archivePathやexportOptionsPlist等は実際のパスにあわせて書き換えてください

ここでのポイントは2つあります。

-exportOptionsPlist

エクスポートするときの設定をplistに記載し、そのパスを指定しています。最低限以下の内容があれば大丈夫です。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>developmentやapp-storeなどをしてください</string>
    <key>destination</key>
    <string>export</string>
    <key>teamID</key>
    <string>ご自身のチームIDを設定してください</string>
    <key>signingStyle</key>
    <string>automatic</string>
</dict>
</plist>

-allowProvisioningUpdates 以下

allowProvisioningUpdatesを指定することでクラウドでprovisioing profileの更新が許可され、Cloud Signingが有効になります

その下のauthenticationKeyPath authenticationKeyID authenticationKeyIssuerID はAppStoreConnectのAPIを利用するために必要なAPIキー等の情報です。ドキュメント等を参考に生成してください。Cloud Signgingを利用するため、APIキーのロールはAdminにしてください(参考: ロールごとの権限の一覧表)。 これらは秘匿値なので別の場所においておき、ワークフローのなかでそれを取得して利用します。

authenticationKeyPathはファイルのパスを指定する必要があります。Github Actionsのシークレットを利用する場合は、base64エンコードした文字列を保存しておき、ワークフローのなかでdecodeしてファイルに書き出すとよいです。

- name: create Auth Key
  run: |
    echo -n '${{ inputs.apple-api-auth-key-base64 }}' | base64 --decode > ./AuthKey_${{ inputs.apple-api-issuer-id }}.p8
  shell: bash

Exportが成功するとipaファイルが生成されるので、テスト配布用のサービスやAppStoreConnectにアップロードできます。

これで地味に面倒な証明書等の更新作業から解放されました!

おわりに

面倒な作業はどんどん自動化して、本来やるべき開発に注力する時間を増やしていきましょう!

もしコドモンのモバイルアプリやフロントの基盤改善にご興味のある方いれば、Pittaからお話できるとうれしいです。

pitta.me

それでは、コドモンアドベントカレンダーの明日の記事もお楽しみに〜!