SwiftUIでpreview機能を活用したいよねとチームで話があり、各viewで使用できるように実装することになりました。
その際に学んだポイントついて、簡単にまとめたいと思います。
APIに依存しない
ViewとViewModel形式で構成されているケース(VVM)を例に考えていきます。この場合、基本的にView側で表示する内容はViewModel側で保持しているPublish変数を展開していくことになると思います。そして、そのPublish変数の中身というのはAPIを通して取得してきたデータを詰めていることと思います。
previewを実装する場合、このPublish変数にAPIを通して詰めるのではなく、自分でpreview用のダミーデータを詰める関数を用意してやることがポイントになります。APIを通して詰めるようにしてしまうと、変更時に毎回APIを叩くことになり、その分待ち時間が発生し、previewの特徴である手早くViewの修正・確認ができるメリットが薄れてしまうことになるからです。最低限この点は意識するようにしましょう。
ダミーデータ生成の関数を用意
まずは上記で話したようにPublish変数にダミーデータを詰める関数を用意しましょう。この関数自体をどこに置くかは議論の余地がありますが、今回はViewModel内で書くこととします。
class ProductViewModel: ObservableObject {
@Published var products: [Product] = [] // 商品一覧
// 商品の型
struct Product: Identifiable {
var id: UUID = .init()
var productId: Int
var productName: String
var productImgUrl: String
}
extension ProductViewModel {
// preview用の商品データ作成
func createProductDummyData() {
for _ in 1 ... 8 {
self.products.append(Product(productId: 1,
productName: "ダミー",
productImgUrl: "https://dummy"))
}
}
}
}
これでダミーデータを作成する関数を用意することができました。ちなみにextension内に記載しているのは実ロジックとの区別をつける為です。この点も議論の余地がありますが、今回はこのやり方で進めていきます。
モデルを外部注入できる作りにする
実装のイメージとしてはpreviewの中でモデルインスタンスを作成し、上記で作成したダミー関数を呼んで予めPublish変数に値を詰めた状態でView側に渡すという形が望ましいです。これを実現するにはモデルインスタンスをView側に外部注入できる作りになっていないといけないのですが、下記のような実装になっていると困難です。
# 修正前
struct ProductView: View {
@StateObject var productViewModel: ProductViewModel
init() {
_productViewModel = StateObject(wrappedValue: ProductViewModel())
}
}
上記の作りではinit時の処理の中で、Modelインスタンスを生成してしまっている為、外部注入ができません。下記のように修正してみましょう。
# 修正後
struct ProductView: View {
@StateObject var productViewModel: ProductViewModel
init(productViewModel: ProductViewModel? = nil) {
_productViewModel = StateObject(wrappedValue: productViewModel ?? ProductViewModel())
}
}
上記の修正により、外部から渡って来なければそのまま新規にインスタンスを作成し、渡ってくればそれをそのまま使用することができるようになりました。これで諸所の準備が完了しましたので、実際にPreview側に書いてみましょう。
# 最終実装
struct ProductView: View {
@StateObject var productViewModel: ProductViewModel
init(productViewModel: ProductViewModel? = nil) {
_productViewModel = StateObject(wrappedValue: productViewModel ?? ProductViewModel())
}
}
#Preview("商品一覧") {
let productViewModel = ProductViewModel()
productViewModel.createProductDummyData()
ProductView(productViewModel: productViewModel)
}
これでAPIを介さずに、Preview機能を使用できるようになりました。簡単ですね
終わりに
以上がPreview機能を実装する際のポイントでした。どんどんpreview機能を活用して、開発効率を上げていけると良いですね。