fastlane

iOSアプリのスクショを自動で撮れるようにしたい

ios-snapshot-auto

iOSアプリのスクショを自動で撮れるようにしたい

アプリをリリースする時のスクショを用意するのがめんどくさい。。

初めてアプリを審査に出そうとした時に、4種類のデバイスのサイズのスクショをアップロードしなきゃいけないとAppStoreConnectに書いてあり、大変だなーと思いながら手動で撮りましたが、なんとか自動でスクショをとれないかなと思い、調べてみました。 実装するための大まかな流れと、つまづいたところをまとめます。

今回の目的

・複数デバイスのスクショを自動で撮る

スクショを使ってやりたいこと
  1. 審査用のスクショを撮る
  2. iPadやiPhoneSEなどサイズの異なるデバイスでレイアウトが崩れていないかを確認する
  3. ja/enなど複数の言語で正しくLocalizeされているかを確認する
  4. 複数のOSのバージョンでレイアウトが崩れていないかを確認する

(本来は1.のみが目的でしたが、欲が出て、どんどん増えていきました。。)

スクショを撮るツールは色々ありますが、上記のやりたいことに加え、

  • Fastlaneを既に入れている
  • UITestを触ってみたい
  • 撮ったスクショが一覧で見れる

などをふまえ、FastlaneSnapshotを使ってみることにしました。 なお私のプロジェクトでは、XcodeGenを使っているので、 それを導入している前提で説明しているので、 使っていない人はそこは飛ばしてください。 またHomeNoteという言葉が時々出てきますが、これは私の個人アプリの名前です。  

ステップ

1.FastlaneとSnapshotの導入

大変なので車輪の再発明を避けるため、この辺の記事を参考すれば、だいたいできるかと思います。

Fastlane
Snapshot

2.Snapshotで追加されるファイルをプロジェクトに追加する。

Snapshotのセットアップが完了すると、SnapshotHelper.swiftが追加されるので、これをプロジェクトに追加するために、
project.ymlのUITestのTargetの項目へ以下のようにSnapshotHelper.swift追加するための記述を追記します。
このプロジェクトでは、SupportingFilesに置いています。

targets:
  HomeNote:
    ...
    sources:
      - path: HomeNote
        name: HomeNote
        excludes:
          - "*/SnapshotHelper.swift"
  HomeNoteUITests:
    ....
     sources:
       - path: HomeNoteUITests
         name: HomeNoteUITests
       - HomeNote/SupportingFiles/SnapshotHelper.swift

3.UITestのコードとスクショを撮るコードを書く

UITestのコードを書いていきます。UITestの書き方については、この記事などを参考にしてください。
UITestではアプリ側のTargetのコードでUIのパーツにIDを設定し、UITestのコードでそのIDを使って各パーツを特定するのですが、それぞれハードコーディングをすると、片方を変更した時にテストが失敗することになるので、共通のIDを使えるようにします。
例えば以下のようなenumを作ります。

enum HogeViewControllerIdentifiers: String {
    static let viewControllerName = "HogeViewController"

    case hogeButton
    case hogeLabel

    var id: String {
        return HogeViewControllerIdentifiers.viewControllerName + rawValue
    }
}

そしてIDの設定を以下のように、テストのときだけ設定されるようにします。
HomeNoteアプリではTest用のConfigurationを設定しているので、以下のようになりますが、設定していない場合は、ドキュメントのこちらを参考にしてください。

extension HogeViewController {
    func setupUITestIdentifier() {
        #if TEST
        hogeButton.accessibilityIdentifier = HogeViewControllerIdentifiers.hogeButton.id
        hogeLabel.accessibilityIdentifier = HogeViewControllerIdentifiers.hogeLabel.id
        #endif
    }
}

そしてUITestのTargetからも上記のenumを呼びさせるように、project.ymlへ追記します。

sources:
      - path: HomeNoteUITests
        name: HomeNoteUITests
      - HomeNote/SupportingFiles/SnapshotHelper.swift
      - HomeNote/Views/HogeViewControllerIdentifiers.swift

最後にUITestのコードを書きます。UITestのファイルに以下のようなメソッドを追加します。

func testSnapshot() throws {
    let app = XCUIApplication()
    // Snapshotのセットアップ(SnapshotHelper.swiftのメソッドを実行)
    setupSnapshot(app, waitForAnimations: false)
    app.launch()
    // 最初に開いた画面のスクショを撮影
    snapshot("A-HogeViewController")
    app.buttons[HogeViewControllerIdentifiers.hogeButton.id].tap() // Present HogeModalViewController
    // hogeButtonをタップして開いた画面のスクショを撮影
    snapshot("B-HogeModalViewController")
}

4. テスト時にMockへ切り替えられるようにする。

AppStoreに表示するようのスクショなので、見た目がそれっぽいデータが固定で表示できるように、スクショを撮るときはMockのデータに切り替えられるようにします。HomeNoteアプリではログイン中はFirebase、ログインしていない時はRealmからデータを取得するようになっていますが、テスト中は取得先をMockManagerに切り替えできるようにします。
Dependency injectionを使ってますが、このアプリではこちらのサイトを参考に、PropertyWrapperを使ったDipendencyInjectionの実装をしています。
以下は例なので、各アプリの実装に合わせていただければと思います。

static var requesterModule: Module {
    return Module { () -> RequesterProtocol in
        #if TEST
        return MockManager()
        #endif
        if authManager.isAuthenticated {
            return FirebaseRequester()
       } else {
            return LocalRequester()
        }
    }
}

5.Snapfileを用意する

Snapshotでスクショを撮る時の設定ファイルを用意します。
以下にHomeNoteアプリのSnapfileを記載します。

# A list of devices you want to take the screenshots from
devices([
  "iPhone 8 Plus",
  "iPhone 11 Pro Max",
  "iPad Pro (12.9-inch) (4th generation)"
])

languages([
  "ja",
  "en-US"
])

# The name of the scheme which contains the UI Tests
scheme("HomeNote_Develop")
ios_version("13.4")

# Where should the resulting screenshots be stored?
output_directory("./fastlane/screenshots")
sdk("iphonesimulator")
erase_simulator(true)

clear_previous_screenshots(true)
localize_simulator(true)
dark_mode(true)

# You can set override_status_bar to true to set the status bar to Tuesday January 9th at 9:41AM with full battery and reception.
override_status_bar(true)
stop_after_first_error(true)
html_template("./fastlane/screenshots/screenshots.html")
skip_open_summary(false)

# 以下を設定することで、上記で設定したUITestのメソッドのみを実行できる
only_testing("HomeNoteUITests/HomeNoteUITests/testSnapshot")

# ビルドをスキップしたい場合は、以下を設定
test_without_building(true)
derived_data_path("path/to/derived_data")

注意点としては、該当するデバイスとOSの組み合わせのシミュレーターがないと、実行に失敗するので、事前に注意しておくといいと思います。
xcrun simctl listというコマンドを実行すると、使用できるシミュレーターの一覧がOSのバージョンとともに表示されるので、便利です。

6.実行!!!

あとはスクショを撮るコマンドを実行するだけです。

bundle exec fastlane snapshot

HomeNoteアプリでは、Makefileに記載し、

make snapshot

で実行できるようにしています。
スクショを撮り終わるまでの時間は、画面数、デバイス数や言語数、ビルドをスキップするかどうかによりますが、そこそこ時間はかかるので、実行した後は別作業、仮眠、ご飯、お風呂、軽い運動などをしながら放置し、テストが終わるのを待ちましょう。

終了すると、Snapfileで指定したPathにスクショのファイルが作成されます。
またそれらを一覧で見れる、以下のようなhtmlファイルも作成されます。

追加実装(おまけ)

1.スクショの一覧をPDFで共有できるようにしたい!

スクショの一覧を他の人に共有したいと思った時に、全てのスクショとhtmlをzipにして、共有するのは手間なので、PDF化して、簡単に共有できるようにしました。
色々調べた結果、wkhtmltopdfを使うことにしました。
Homebrewでインストールする

brew cask install wkhtmltopdf

あとはPDF化する処理を記述する。このアプリではMakefileに記載してます。これでscreenshots.pdfが作成されます。

pdf:
  wkhtmltopdf --title HomeNote-Snapshot --enable-local-file-access ./fastlane/screenshots/screenshots.html ./fastlane/screenshots/screenshots.pdf

ちなみに--enable-local-file-accessをつける理由は、こちらを参考にしてください。

2.複数OSバージョンのスクショをまとめて撮れるようにしたい。

標準のSnapshotだと1バージョンしかスクショを撮れないので、複数のOSのバージョンのスクショを撮ろうとすると、ios_versionを1回ずつ切り替えて実行する必要があるので、できなくはないですが、かなり手間です。
そこで複数OSのスクショをまとめて撮れるツール、MultiOSVersionSnapshotを作成してみました。単純に内部でSnapshotをループさせているだけです。Mintでインストールできます。
Mintfileにu5-03/MultiOSVersionSnapshotを追加し、

mint bootstrap

を実行します。
Snapfileのような設定ファイル、MultiOSVersionSnapshot.ymlを一番上の階層に作成します。サンプルはこちらを確認します。
あとは以下のコマンドを実行すれば、後は待つだけです。

mint run MultiOSVersionSnapshot MultiOSVersionSnapshot

以下の様に、OSのバージョンごとにスクショをまとめたフォルダが作成されます。

まとめ

スクショをいちいち撮る苦労から解放されたのは良かったと思います。
UIチェックについては、今後はXcodeのプレビュー(主にSwiftUIで使用)を使えば、そこまで時間がかからなくなるので、わざわざ使わなくてもいいかなと思ったりもしましたが、UIテストの方法についてもついでに確認できたので、ためになったと思います。

参考

+1

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA