SwiftUIのPreviews機能を利用するとシミュレータを起動することなくViewの動作を確認できます。
Generate dynamic, interactive previews of your custom views.
Previews in Xcode | Apple Developer Documentation
プレビューを利用して開発する中で、プレビューのスナップショットが一覧で見られると便利だと思ったので、それを行うライブラリを作ってみました。
※ SwiftUIのプレビュー機能を利用しているViewが対象となります。プレビューされていないViewのスナップショットは作成されません。
つくったもの
yyokii/UIPreviewCatalog というものを作りました。
createCatalog(previewItems:)
を実行すると以下のようにPreviewの画像一覧とそれらをまとめたマークダウンファイルが出力されます。
これはライブラリであり、対象のアプリへの導入が必要です。
しかし、もしこれがCLIツールやMacアプリとして提供できれば導入が1度で終了します。
そのような方針も検討したのですが、実現方法が分からなかったので今回は断念しました。
開発の動機
開発において以下のように感じることはないでしょうか。
-
仕事で新しいプロジェクトがはじまったけど、このアプリではどんなViewが使われているのだろう
-
オープンソースのアプリをコードリーディングしてView作成の参考にしたいから、どんなViewが作られているかざっとみてみたいなあ
これらはアプリを実際に動かせばだいたい解決します。
しかし手動ですと漏れが生じることがありますし、操作を面倒に思うかもしれません。
そこで、SwiftUIでプレビューをしているならそれを一覧化すれば役に立つのではないかと思ったので作ってみようと思いました。
つまり、「アプリで使われてるViewをざっと見られる」ものをコードで作り、
- プロジェクトのキャッチアップ
- 同じようなViewを作ってしまうことの防止
- 新しくViewを作成する際に似たようなViewが既にあればそれに気付きやすくなること
などに役立てようと思いました。
同じような目的でUIに関するドキュメントを書類としてまとめていたり、デバッグビューを作成しているプロジェクトもあるでしょう。素晴らしいです。
本ライブラリはそこまではできていないけど、簡易的なものを出力してみたい、といったユースケースに合うものと思います。
なお、このアイデアはとあるコミュニティで輪読会のイベントをしており、その議論の中で出たものを参考にしています。
元々は「各画面とその画面で利用されているクラス等の情報がマークダウンで出力されるといいよね」というものでしたが、SwiftUIのプレビュー情報に限ってなら比較的簡単にできそうと思い、元のアイデアを少し変更して作りました。
輪読会のイベントとそこでの議論がなければ開発に至りませんでした。参加されていた人には感謝しています
使い方
大きく3つの手順があります。
- ライブラリの導入
-
PreviewItem
配列の生成 - スナップショットとMarkdownファイルの生成
1. ライブラリの導入
アプリの機能に影響しないものなので、テストターゲットに依存関係を追加します。
ユニットテストで実行が可能です。
Swift Package Managerを利用してインストール可能です。
XcodeやPackage.swift
マニフェストを通じてインストールできます。
2. PreviewItem
配列の生成
このライブラリにPreviewItem
というstruct
があり、それがスナップショットを作成する対象となります。
以下のように自分で定義することもできますが、既存のコード生成ツールを使用して簡略化することもできます。
作成したファイルはテストターゲットに含まれれるようにします。
import SwiftUI
import UIPreviewCatalog
@testable import DemoUIPreviewCatalog
let previewItems: [PreviewItem] = [
.init(name: "BlueView_Previews", previews: BlueView_Previews._allPreviews),
.init(name: "ContentView_Previews", previews: ContentView_Previews._allPreviews),
]
Sourceryを利用してコード生成
すでに完成しているアプリにおいて、上記のように手動でPreviewItem
配列を作成するのは面倒です。そこで、Sourceryを利用してPreviewProvider
に準拠しているstruct
を抽出し、PreviewItem
配列を生成してみます。
まずSourceryをインストールします。
そしてソース生成用のテンプレートファイルを作成します。
PreviewItem.stencilがテンプレートファイルですので、こちらの中身と同じものを作成してください。
そしてSourceryのコマンドを利用してコードを生成します。
以下にサンプルを示します。
$ sourcery \
--sources /Users/{Foo}/Desktop/DemoUIPreviewCatalog/DemoUIPreviewCatalog \
--templates /Users/{Foo}/Desktop/PreviewItem.stencil \
--output /Users/{Foo}/Desktop/DemoUIPreviewCatalog/DemoUIPreviewCatalogTests \
--args mainTarget=DemoUIPreviewCatalog
--sources
: ソースの解析対象のパスを指定します。ここではアプリのメインターゲットが指定されています。
--templates
: テンプレートファイルのパスを指定します。先ほど作成したものを利用します。
--output
: 生成されたコードを配置するパスを指定します。テストターゲットに含まれるようにしています。
--args
: 任意の変数を与えます。生成されたファイルはテストターゲットに配置されますが、メインターゲットのPreview情報を利用しているのでその参照を解決するために@testable import DemoUIPreviewCatalog
と記述する必要があります。従ってここではアプリのメインターゲットのモジュール名を指定しています。
これを実行することで次のようなファイルが生成されます。
import SwiftUI
import UIPreviewCatalog
@testable import DemoUIPreviewCatalog
let previewItems: [PreviewItem] = [
.init(name: "BlueView_Previews", previews: BlueView_Previews._allPreviews),
.init(name: "ContentView_Previews", previews: ContentView_Previews._allPreviews),
]
3. スナップショットとMarkdownファイルの生成
XcodeのEdit Schemeで、環境変数を設定します。
Name : PREVIEW_CATALOG_PATH
Value : 画像とマークダウンの出力先です。例えば、 $(SOURCE_ROOT)
。
スナップショットとMarkdownファイルを出力します。
下記のようにcreateCatalog(previewItems:)
を実行することでファイルが出力されます。
func testOutputUIPreviewCatalog() {
let catalog = UIPreviewCatalog(config: .defaultConfig)
do {
try catalog.createCatalog(previewItems: previewItems)
} catch {
print(error.localizedDescription)
XCTFail()
}
}
以上の内容はREADMEにも記載しています。
課題
現状アニメーションがついているViewに対して実行すると、空白になってしまいます。
もしアイデアがありましたら、コメントやプルリクエスト頂けると嬉しいです!
Animation Support · Issue #6 · yyokii/UIPreviewCatalog
おわりに
本内容が誰かの参考になると幸いです。
良いなと思ったらスター頂けると嬉しいです🌟
yyokii/UIPreviewCatalog