iOSアプリのスクショを自動で撮れるようにしたい
アプリをリリースする時のスクショを用意するのがめんどくさい。。
初めてアプリを審査に出そうとした時に、4種類のデバイスのサイズのスクショをアップロードしなきゃいけないとAppStoreConnectに書いてあり、大変だなーと思いながら手動で撮りましたが、なんとか自動でスクショをとれないかなと思い、調べてみました。 実装するための大まかな流れと、つまづいたところをまとめます。
・複数デバイスのスクショを自動で撮る
- 審査用のスクショを撮る
- iPadやiPhoneSEなどサイズの異なるデバイスでレイアウトが崩れていないかを確認する
- ja/enなど複数の言語で正しくLocalizeされているかを確認する
- 複数のOSのバージョンでレイアウトが崩れていないかを確認する
(本来は1.のみが目的でしたが、欲が出て、どんどん増えていきました。。)
スクショを撮るツールは色々ありますが、上記のやりたいことに加え、
- Fastlaneを既に入れている
- UITestを触ってみたい
- 撮ったスクショが一覧で見れる
などをふまえ、FastlaneのSnapshotを使ってみることにしました。 なお私のプロジェクトでは、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テストの方法についてもついでに確認できたので、ためになったと思います。