Xcode 15のPreview Macroにおける依存注入について
本記事はSwiftWednesday Advent Calendar 2023の2日目の記事です。
本記事では、Xcode 15から利用可能なPreview Macroにおける依存注入について紹介します。
はじめに
Xcode 15から、開発者は新たなPreview Macro
機能を利用できるようになりました。この機能を利用することで、これまで利用されてきたPreviewProvider
と比較して、プレビューの表示のための実装をより簡単に実装することができるようになりました。一方で、View
の依存をDIする際にPreviewProvider
の時と同様の書き方をすることは難しくなりました。この記事では、Preview Macro
利用時の依存注入(DI)のアプローチを2つの方法で紹介します。
View
について
前提となる初めに、基本となるView
の構造について説明します。例として、ユーザー情報を表示するView
を考えます。
struct ContentView: View {
let user: User
var body: some View {
// ユーザー情報を表示する
}
}
このViewでプレビューを行う際、ユーザー情報を依存注入(DI)する必要があります。また、異なる状態をプレビューするために、複数のプレビューインスタンスを用意します。しかし、下記のように毎回ユーザーのインスタンスを生成する方法では、同様のコードを繰り返すため、可読性の観点で少し読みづらいという問題があります。
#Preview {
ContentView(
user: User(
name: "hoge",
age: 23,
someBoolValue: false
anotherBoolValue: true
)
)
}
#Preview {
ContentView(
user: User(
name: "hoge",
age: 23,
someBoolValue: true
anotherBoolValue: true
)
)
}
#Preview {
// ...
}
この問題を解決するために、makeUser
というファクトリーメソッドを導入します。このメソッドを使用して、異なるプレビューを簡単に生成できます。
func makeUser(someBoolValue: Bool, anotherBoolValue) -> User {
.init(
name: "hoge",
age: 23,
someBoolValue: someBoolValue
anotherBoolValue: anotherBoolValue
)
}
方法1: Privateメソッドとしての切り出し
最初の方法は、makeUser
メソッドをprivateメソッドとしてContentView
のファイル内に切り出すことです。このアプローチの利点は、コードの構造が明確になり、再利用性が向上することです。一方で、デメリットとして、メソッドがView
の外部に存在することでコードの一貫性が損なわれる可能性があります。
struct ContentView: View { ... }
private func makeUser(someBoolValue: Bool, anotherBoolValue) -> User {
.init(
name: "hoge",
age: 23,
someBoolValue: someBoolValue
anotherBoolValue: anotherBoolValue
)
}
#Preview {
ContentView(
user: makeUser(someBoolValue: true, anotherBoolValue: true)
)
}
#Preview {
ContentView(
user: makeUser(someBoolValue: false, anotherBoolValue: false)
)
}
#PreView {
// ...
}
DeveloperToolsSupport
のextension
としての切り出し
方法2: 二つ目の方法は、makeUser
メソッドをDeveloperToolsSupport.Preview
のextension
として切り出すことです。この方法のメリットは、プレビュー固有のロジックがDeveloperToolsSupport.Preview
の内部に独立して管理されることにあります。
private extension DeveloperToolsSupport.Preview {
static func makeUser(/* ... */) -> User {
// 略
}
}
#Preview {
ContentView(
user: DeveloperToolsSupport.Preview.makeUser(/* ... */)
)
}
#PreView {
// ...
}
番外編: PreviewProviderを利用する
PreviewProvider
を利用する方法も有効です。ContentView_Previews
内にmakeUser
メソッドを定義し、プレビューをグループ化して管理することで、コードの構造が整理され、再利用が容易になります。
struct ContentView_Previews: PreviewProvider {
private static func makeUser(/* ... */) -> User {
// ...
}
static var previews: some View {
Group {
ContentView(user: makeUser(/* ... */)
ContentView(user: makeUser(/* ... */)
}
}
}
おわりに
Xcode 15のPreview Macro
機能を利用することで、これまでよりも簡単にプレビューの実装を記述することが可能になります。本記事で紹介した方法を活用することで、Preview Macro
を用いたDIの実装を簡略化することができます。各方法には特定の利点と使用シナリオがありますので、プロジェクトのニーズに合わせて最適な方法を選択することが大切です。
一方で、プロジェクトの要件によっては、従来のPreview Provider
を使用したアプローチの方が適切な場合もあります。Preview Macro
の新機能と従来の方法の両方を理解し、プロジェクトに最も適したアプローチを選択することで、高品質でメンテナンスしやすいiOSアプリの開発が実現できると考えています。
明日は @uhooi さんです!
開発環境
- Swift compiler version info:
Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
- Xcode version info:
Xcode 15.0 Build version 15A240d
Discussion