概要
SwiftUIにおけるViewの中で、複数のクロージャを扱うケースは多々あると思います。
この記事では、そのようなクロージャの管理をenumで行うという提案とその実装方法の紹介をしたいと思います。
現状の課題
SwiftUIで開発を行なっていると、以下の様なケースがよくあると思います。
- 子Viewで発生したユーザーアクションをトリガーに、親ViewのViewModelのメソッドを実行させたい
この様なケースではクロージャを使うことが一般的だと思います。
具体的には、以下のようなコードになります。
struct ParentView: View {
let viewModel = ParentViewModel()
var body: some View {
VStack {
Text("Parent View")
ChildView {
viewModel.someFunction()
}
}
}
}
struct ChildView: View {
let someAction: () -> Void
var body: some View {
VStack {
Text("Child View")
Button {
someAction()
} label: {
Text("Button1")
}
}
}
}
class ParentViewModel {
func someFunction() {}
}
クロージャが一つであれば、特に問題なさそうですね。
僕もこの実装で特に問題ないと思います。
しかし、ChildViewで受け取りたいactionが増えるとどうなるでしょうか?
struct ParentView: View {
let viewModel = ParentViewModel()
var body: some View {
VStack {
Text("Parent View")
ChildView {
viewModel.some1Function()
} some2Action: {
let value: Int = 100
viewModel.some2Function(value: value)
} some3Action: { value in
viewModel.some3Function(value: value)
}
}
}
}
struct ChildView: View {
let some1Action: () -> Void
let some2Action: () -> Void
let some3Action: (String) -> Void
let someValue: String = "hoge"
var body: some View {
VStack {
Text("Child View")
Button {
some1Action()
} label: {
Text("Button1")
}
Button {
some2Action()
} label: {
Text("Button2")
}
Button {
some3Action(someValue)
} label: {
Text("Button3")
}
}
}
}
class ParentViewModel {
func some1Function() {}
func some2Function(value: Int) {}
func some3Function(value: String) {}
}
途端に、以下の様な課題が発生してしまうと思います。
- 可読性の低下
- ChildViewのプロパティの増加
上記の例はまだクロージャ自体がシンプルなため比較的問題は軽微ですが、クロージャの行数が多くなると可読性の問題は益々悪化していくことが予想出来ます。
打ち手
この課題をenumで管理することによって解決したいと思います。
まずはChildView側の実装です。
以下のようにenumを作成し、enumでactionを管理するようにします。
enum ChildViewEvents {
case some1Action
case some2Action
case some3Action(String)
}
struct ChildView: View {
let event: (ChildViewEvents) -> Void
let someValue: String = "hoge"
var body: some View {
VStack {
Text("Child View")
Button {
event(.some1Action)
} label: {
Text("Button1")
}
Button {
event(.some2Action)
} label: {
Text("Button2")
}
Button {
event(.some3Action(someValue))
} label: {
Text("Button3")
}
}
}
}
次にParentViewを以下のように実装します。
struct ParentView: View {
let viewModel = ParentViewModel()
var body: some View {
VStack {
Text("Parent View")
ChildView { events in
switch events {
case .some1Action:
viewModel.some1Function()
case .some2Action:
let value: Int = 100
viewModel.some2Function(value: value)
case .some3Action(let value):
viewModel.some3Function(value: value)
}
}
}
}
}
このようにクロージャをswitchで管理することが出来るようになり、可読性が向上したと共に、ChildView側のプロパティ数も減らすことが出来ました。
終わりに
View内の複数のクロージャをenumで管理しようという提案はいかがだったでしょうか?
可読性も上がったとともに、個人的には非常にスマートな実装になっているのではないかと感じており、今後はView内の複数のクロージャをenumで管理していきたいと思いました。
機会があれば、是非ご自身のプロジェクトでもこの実装を試して頂けると幸いです。
最後までお読み頂き、ありがとうございました。
参考