[iOS]大規模プロダクトにfastlane matchを導入した話

# モチベーション
iOSエンジニアにおいて，証明書は避けて通れない壁だと考えております．
今回はこの壁について，大規模プロダクトへ導入する際の課題・解決方法・導入手順をお話できればと思います．

本記事の立ち位置としては，[新卒研修時に取り組んだ内容](https://qiita.com/ARATAYOKOYAMA/items/2ef13e15b548c0ef89d6) の延長戦といった形です．

# 課題
証明書を手動管理している
1. 1年に1度，証明書を手動で更新する必要がある
2. 開発メンバーや開発PCを追加する度に，Certificatesを作りProvisioningProfilesを更新する必要がある
3. 新規検証端末を追加する度に，ProvisioningProfilesを更新する必要がある

上記による課題は何か
1. 手動で更新する時間と手間がかかる．また，**ヒューマンエラーが起こる可能性**がある
2. オンボーディングに時間がかかってしまう．**特にインターン生にとっては，本質的ではない箇所に時間を割く必要がある**
3. 新しい端末を追加するたびに，更新して再アップロードする必要がある

# 課題の解決方法
fastalne matchを導入して解決する
導入後は，上記の課題が下記のように解決される
1. 指定のコマンドを叩くだけであり，再アップロード等は必要ない
2. 指定のコマンドを叩いてDLするだけであり，短時間で導入と更新が完了する
3. 指定のコマンドを叩して端末を追加する．追加後は，指定のコマンドを叩くようチームにアナウンスする

# matchの導入手順
## ローカルPC上で動作させる
### 開発メンバーが共通して使用できるAppleIDを作成する
**Tips.** 特定のメンバーのIDではなく，別途作成することにより，メンバーが変わった後も問題なく使用できる．
作成したAppleIDに対して，App Managerの役割を割り当てる

App Store Connect >「ユーザーとアクセス」> App > 該当アプリケーション
デベロッパリソース > Certificates, Identifiers & Profiles（証明書、ID、プロファイル）へのアクセス

https://help.apple.com/app-store-connect/?lang=ja-jp#/deve5f9a89d7
> 「Certificates, Identifiers & Profile (証明書、ID & プロファイル)」へのアクセスは、組織のチームのメンバーである、App Manager または Developer の役割を持つユーザに追加で割り当てることができる特権です。この権限が追加されたユーザは、すべての App に関連付けられた証明書、ID、およびプロファイルを表示できます。

### 初めてアプリを作成する場合は， App IDの確保を確保する
```
fastlane produce -a bundleidentifder -i
```
-iはStoreでの作成をスキップ
例えば，

```
fastlane produce -a com.hoghoge.test -i
```

### matchの初期設定

```
fastlane match init
```

fastlnae/Matchファイルが作成されます

### Matchfileを編集する

```
Matchfile
 app_identifier(["tools.fastlane.app", "tools.fastlane.app2"])
 username("user@fastlane.tools") # 作成したAppleID
```

**app_identifierは単数でも複数でもOK**
単数の場合は，`app_identifier("tools.fastlane.app")`

### 証明書を管理するGIthubのリポジトリを用意する
**Tips.** 証明書を管理する場所として，s3なども選択できますがgithubがオススメです

作成するリポジトリは，**必ずprivateリポジトリにしましょう**
証明書などのセンシティブなファイルはパブリックにするべきではありません

**Note.**  Githubとの接続方法は，sshにしてください．httpsでは後で上手くいきません

### 作成
develop

```
fastlane match development
```

adhoc

```
fastlane match adhoc
```

store

```
fastlane match appstore
```

初めて作成する時は，作成するPCのパスワードを求められます
2回目以降は下記を参照しているはずです
` /Users/username/Library/Keychains/login.keychain-db`

証明書の管理場所として，先程作成したリポジトリのURLを入力します．
このとき，必ずssh版のURLにしてください．
その後，証明書を管理するパスワードを入力します．
ここでいうパスワードとは，証明書を追加・更新・取得するためのパスワードです

**Tips.** ここで入力したパスワードの管理方法は予めチームで決めておいて下さい．

**Note.** matchでは，Certificatesを1種類につき1つしか持てません．なので，既にCertificatesがある方は，削除する必要があります．そして，Certificatesが削除された瞬間に，該当のProvisioningProfilesは失効します．覚悟してください．
ただし，失効してもStoreでの配信が直ちに停止するわけではありません．

https://developer.apple.com/jp/support/certificates
>iOS配布用証明書（App Store）
> Apple Developer Programのメンバーシップが有効な場合は、App Storeで公開されている既存のAppに影響が及ぶことはありません。ただし、期限切れまたは無効になっている証明書を使用して署名した新しいAppやアップデート版は、App Storeに提出できなくなります。
> iOS配布用証明書（社内用App）
> この証明書を使用して署名されたAppは、今後実行できなくなります。新しいバージョンのAppは、新しい証明書で署名して配信する必要があります。

### 取得
develop

```
fastlane match development --readonly
```

adhoc

```
fastlane match adhoc --readonly
```

store

```
fastlane match appstore --readonly
```

**Tips.** `--readonly`は読み取り専用を意味します．証明書を作成する方以外は，`--readonly`を付けて実行しましょう

### 削除
development

```
fastlane match nuke development
```

adhoc and store

```
fastlane match nuke distribution
```

削除できたら，再度作ります
**Tips.** 特定のファイルだけを消すことはできません．初めて導入する時にのみ実行することをオススメします

### 更新
証明書が切れると下記のエラーが出ます
>[!] Your certificate 'XXXXXXXXXX.cer' is not valid, please check end date and renew it if necessary

更新方法は大きく分けて2つあります
1. nukeコマンドを叩いた後に，再度作成する
2. Githubのリポジトリをクローンして該当のファイルを削除して，pushする

> **Tips.** 特定のファイルだけを消すことはできません．初めて導入する時にのみ実行することをオススメします
の理由から，2. Githubのリポジトリをクローンして該当のファイルを削除して，pushする をオススメします

### 検証端末の追加

```
fastlane run register_device name:"<name>" udid:"<udid>"
```
追加後は再取得する

## CI上で動作させる
### Fastfile, Matchfile, Gymfileを編集する
CI上で動作させるには，上記に加えてもう少し作業が必要です
下記は
[XcodeでDevelop/Staging/Release環境を上手に切り分ける方法](https://qiita.com/Todate/items/a2e6a26731c79bd23e02)
を参考に，development, adhoc, storeで環境を分けています

また，下記の例はadhocを想定していますが，store版も同様に必要です

```
# Fastfile
 lane :adhoc_archive do
 			// 署名用のkychain作成とmatchを追記する
 			if is_ci?
 				create_keychain(
 				name: ENV['MATCH_KEYCHAIN_NAME'],
 				password: ENV['MATCH_KEYCHAIN_PASSWORD'],
 				default_keychain: true,
 				timeout: 1800,
 			)
 			end
 			match
 			gym // build_ios_appと同義

```

```
# Matchfile
 for_lane :adhoc_archive do
     app_identifier "com.hoghoge.test"
     type "adhoc"
     readonly true
 end
```

```
# Gymfile
 for_lane :adhoc_archive do
     scheme "mtchtests"
     configuration 'Release_Adhoc'
     export_method "ad-hoc"
     export_options({
         provisioningProfiles: {
             "com.hoghoge.test" => "match AdHoc com.hoghoge.test"  // ここを出来上がったprovisioningProfilesの名前にする
         }
     })
 end

```

**Tips.** ENVは環境変数です． **証明書を追加・更新・取得するためのパスワード**とセットで，設定しておきましょう

ここまでできて，ようやくCI上でも動作させる環境が整いました！
この記事では，fastlaneの詳しい設定方法やCIのセットアップはご紹介しません

# プロダクトへの導入段階
1. チームで意思決定をする
各種パスワードの管理方法や，導入スケジュールをすり合わせておきましょう

2.  developからスタートする
最も影響範囲が小さいです．Matchfileは出来上がりますが，adhocやstore版に影響がありません．
また，Gymfileを含めたfastlane周りのファイルを変更する必要がありません
小さく始めましょう

3. adhocとstoreも移行する
ここが最高に緊張感が高いです．
なぜなら，adhoc配信が止まるので，QAメンバーが困る + 最悪次回リリースが止まります．
大きく深呼吸して望みましょう

**Tips.** enterprise版が利用できるプロダクトは，他開発メンバーとQAメンバーにenterprise版を利用するようにアナウンスしましょう．また，導入スケジュールを事前にアナウンスして混乱を最小限にしましょう

# その他のTips
- 取得コマンド等は，Makefiileでまとめておくと楽です
- 本番を想定して，近しい環境で予め試しておくことが大切です

# 実際に導入してみて
個人開発や新規開発では試行錯誤しながら導入できますが，既に稼働している大規模プロダクトではそうもいきません．念入りな準備と覚悟が必要です

[[iOS] Bitrise + fastlane + App Distributionで配布環境を作成](https://qiita.com/ARATAYOKOYAMA/items/2ef13e15b548c0ef89d6) の延長戦のような気分で実践しましたが，緊張感が個人開発とは天と地の差がありました

ただそれでも，掲げた課題を解決することができてよかったです

# 参考文献
- [Apple公式ドキュメント AppStoreConnect](https://help.apple.com/app-store-connect/?lang=ja-jp#/deve5f9a89d7)
- [Aplle公式ドキュメント Certificates](https://developer.apple.com/jp/support/certificates)
- [XcodeでDevelop/Staging/Release環境を上手に切り分ける方法](https://qiita.com/Todate/items/a2e6a26731c79bd23e02)
- [fastlane match を使用して iOS の証明書管理を行う](https://qiita.com/kotarella1110/items/840af2cf80aaea1fb035)
- [[iOS] Bitrise + fastlane + App Distributionで配布環境を作成](https://qiita.com/ARATAYOKOYAMA/items/2ef13e15b548c0ef89d6)