iOSアプリ開発自動化Tips

この記事は[くふうカンパニー Advent Calendar 2018](https://qiita.com/advent-calendar/2018/kufu)の6日目の記事です。

---

手作業を減らせばミスが減り、開発に集中する時間が生まれるため、なるべく自動化したいところです。
そんな思いで行なっている、iOSアプリ開発における自動化のTipsを共有したいと思います。

# fastlane
モバイルアプリ開発を便利にしてくれるRuby製のツールです。
自分はプロジェクト直下にGemfileを置いて`bundle install --path vendor/bundler`を叩いて（bundlerが入っていない方は`gem install bundler`を事前に行なっておく）gemを入れて導入しています。楽です。

ここからは自分が普段行なっている作業を書いていきます。

## リリース作業
リリース作業というと

- バージョンを変える
- ビルド番号を変える
- アーカイブする
- AppStoreConnectにアップロード
- git関連（commitしたりtag打ったり）

あたりが必須の作業になると思いますが、手作業だと面倒だしミスする可能性もあります。
fastlaneを使うと

- `increment_build_number`
- `upload_to_app_store`

などactionが用意されており、それをFastfileに書いていくとリリース用のlaneが出来上がります。
以下参考のlane.
`bundle exec fastlane release ver:{#ver（例: 1.0.0）}`とコマンドを叩くと順に実行されていきます。

```ruby
  before_all do
    ENV["SLACK_URL"] = "your slack url"
  end

  desc "App Store Connectにアップロード"
  lane :release do |options|
    number = increment_build_number
    # verを指定しなければpatchが上がる
    ver = increment_version_number(
      version_number: options[:ver]
    )
    cocoapods
    carthage(
      use_binaries: false,
      platform: "iOS",
      configuration: "Release",
      cache_builds: true
    )
    build_ios_app(
      scheme: "Release"
    )
    # ENV["APPLE_ID"]をアップロードを行う人は自分の端末に設定しておくこと
    # screenshotsはUITestで使っているためスキップ
    upload_to_app_store(
      force: true,
      skip_metadata: true,
      skip_screenshots: true
    )
    slack(
      message: "アップロード完了"
    )

    message = "ver#{ver}(#{number})をアップロード"
    git_add
    git_commit(
      path: "./*",
      message: message
    )
    add_git_tag(
      tag: "#{ver}"
    )
    push_to_git_remote
    # GITHUB_API_TOKENを環境変数として登録しておくとターミナルで聞かれなくて楽
    create_pull_request(
      repo: "your repository",
      title: message
    )
    sh("say", "-v", "Kyoko", "おめでとうございます。リリース作業が完了しました。")
  end
```

## GithubにPull Requestを送る
git関連の作業は全部fastlaneでやってしまえば良いのでは？　という思いつきから作ったのですが意外と便利です。
こういう使い方をfastlaneでやっている人はあまり見かけませんがカスタマイズして使ってみてください。

```ruby
  desc "developブランチへのpull requestを作る"
  lane :pr do |options|
    message = options[:message]
    git_add
    git_commit(
      path: "./*",
      message: message
    )
    push_to_git_remote
    title = options[:title] || git_branch
    create_pull_request(
      repo: "your repository",
      base: "develop",
      title: title
    )
  end
```

## dsymのダウンロード・アップロード
Appleが推奨しているのでbitcodeを有効にしているのですが、後述するクラッシュ計測ツールの分析がそのままだと行えません。dsymをダウンロードしてアップロードして、というのも面倒なのでlaneを用意しました。

```ruby
  desc "Crashlyticsにdsymをアップロードする"
  lane :dsyms do
    download_dsyms                  # Download dSYM files from iTC
    upload_symbols_to_crashlytics   # Upload them to Crashlytics
    clean_build_artifacts           # Delete the local dSYM files
  end
```

参考： https://krausefx.com/blog/download-dsym-symbolication-files-from-itunes-connect-for-bitcode-ios-apps

## 自動スクリーンショット生成
端末ごとにどんな画面になっているのか見たい場面というのはあります。
だいたい下記の記事を参考にしてもらえると良いです。
`bundle exec fastlane snapshot`を叩いてある程度時間が経つとSnapfileで設定した端末のスクショが吐き出されていて気持ち良いです。

参考： https://docs.fastlane.tools/getting-started/ios/screenshots/#setting-up-snapshot

# LicensePlist
アプリの中で利用しているOSSライブラリを、iOS標準の設定アプリに載せるというのが慣習となっています。
ただ、これを自作するのは結構手間がかかります。
そんな問題を解決しているのが**LicensePlist**です。
約1300Starを獲得している素晴らしいツールです。(Star数は2018/12/6時点)

導入はとても楽です。
自分はHomebrewで入れているのですがCocoaPodsでも入れられるそうです。
Homebrewでinstallした後にxcodeprojファイルの`Build Phases`から`Run Script Phase`を選択してスクリプトを追加するとアプリビルド時に更新のあったライブラリを検知してライブラリ一覧を生成・更新してくれます。

詳しくは作者様のGithubのREADMEをどうぞ。
https://github.com/mono0926/LicensePlist

# GAS + SlackでAppStoreConnectのステータスを通知
参考にしたのは[こちら](https://speakerdeck.com/motokiee/iosfalsezi-dong-hua-toshi-zu-mihua-kodotoshe-ji-niji-zhong-suru?slide=39)のスライドです。
AppStoreConnectのメールが来ないけど審査状況が気になる人、メールは届くのだけれどメールよりもSlackに通知される方が便利だと感じる人がそれなりにいたのでGoogleAppsScriptで書きました。
以下参考。

```javascript
function onGotMail(subject, appname) {
  try {
    var threads = GmailApp.search(subject);
    var messages = GmailApp.getMessagesForThreads(threads);
    
    for (var i in messages) {
      for (var j in messages[i]) {
        var message = messages[i][j];
        message.markRead();
        var subject = message.getSubject();
        var body = message.getBody();
        
        // Version Numberはリジェクトメールに含まれないのでここでmatch判定
        if (subject.match(/New Message from App Store/)) {
          postSlack("Appleからのメールを確認しましょう。", appname);
        }
        
        var matched = body.match(/Version Number:\s+(\d+.\d+.\d+|\d+.\d+)/);
        if (matched) {
          var username = "ver" + matched[1] + appname;
          if (subject.match(/has completed processing/)) {
            postSlack("申請作業ができるようになりました！ TestFlightで確認してから申請作業を行いましょう！", username);
          } else if (subject.match(/Waiting For Review/)) {
            postSlack("審査に出しました。1,2日ほどお待ちください。", username);
          } else if (subject.match(/In Review/)) {
            postSlack("審査中です……", username);
          } else if (subject.match(/Pending Developer Release/)) {
            postSlack("審査が通りました！ AppStoreで公開しましょう！", username);
          } else if (subject.match(/Ready for Sale/)) {
            postSlack("公開作業が完了しました！ AppStore反映まで1, 2時間ほどお待ちください。", username);
          }
        }
      }
    }
  } catch(error) {
    postSlack("GASでエラーが発生しました。エラーログ用のスプレッドシートを確認してください。", appname);
    logging(JSON.stringify(error));
  }
}
```

# その他外部ツール
## AppFollow
アプリのレビューやAppStoreでの検索キーワードの順位を教えてくれるツール。
Slackに流しています。無料プランで結構十分です。

## AmazonPinpoint, Firebase
モバイル分析ツール。どの画面を見てどのような動きをしたか計測できます。
Firebaseの方がダッシュボードが見やすく使っている人が多いので情報も豊富ですが、Amazon Pinpointの方はセッションヒートマップが見れたり定期的なプッシュ通知が送れたりします。
どちらも一長一短あるなという印象です。

## Crashlytics
クラッシュ解析ツール。ユーザーがアプリでクラッシュしたらこちらに飛んでくるので、バグをより早く減らせるようになりました。
どの画面でなんのイベントを行ったか、端末のOSやアプリバージョンなどがわかるのでそれを元に調査することとなります。

# まとめ
いかがでしたでしょうか？
余力があればCIやSwiftlintなども導入してみると良いと思います。
ガンガン自動化して開発に集中しましょう！

# 参考サイト一覧
- [fastlane](https://fastlane.tools/)
- [Automatically download and upload dSYM symbolication files from iTunes Connect for Bitcode iOS apps using fastlane](https://krausefx.com/blog/download-dsym-symbolication-files-from-itunes-connect-for-bitcode-ios-apps)
- [fastlane screenshots for iOS and tvOS](https://docs.fastlane.tools/getting-started/ios/screenshots/#setting-up-snapshot)
- [LicensePlist](https://github.com/mono0926/LicensePlist)
- [iOSの自動化と仕組み化 ~コードと設計に集中する~](https://speakerdeck.com/motokiee/iosfalsezi-dong-hua-toshi-zu-mihua-kodotoshe-ji-niji-zhong-suru)