LoginSignup
2
2

【SwiftUI】View内の複数のクロージャはenumで管理しよう!

Last updated at Posted at 2024-04-06

概要

SwiftUIにおけるViewの中で、複数のクロージャを扱うケースは多々あると思います。
この記事では、そのようなクロージャの管理をenumで行うという提案とその実装方法の紹介をしたいと思います。

現状の課題

SwiftUIで開発を行なっていると、以下の様なケースがよくあると思います。

  • 子Viewで発生したユーザーアクションをトリガーに、親ViewのViewModelのメソッドを実行させたい

この様なケースではクロージャを使うことが一般的だと思います。
具体的には、以下のようなコードになります。

ParentView.swift
struct ParentView: View {
    let viewModel = ParentViewModel()
    
    var body: some View {
        VStack {
            Text("Parent View")
            ChildView {
                viewModel.someFunction()
            }
        }
    }
}
ChildView.swift
struct ChildView: View {
    let someAction: () -> Void
    
    var body: some View {
        VStack {
            Text("Child View")
            Button {
                someAction()
            } label: {
                Text("Button1")
            }
        }
    }
}
ParentViewModel.swift
class ParentViewModel {
    func someFunction() {}
}

クロージャが一つであれば、特に問題なさそうですね。
僕もこの実装で特に問題ないと思います。
しかし、ChildViewで受け取りたいactionが増えるとどうなるでしょうか?

ParentView.swift
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)
            }
        }
    }
}
ChildView.swift
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")
            }
        }
    }
}
ParentViewModel.swift
class ParentViewModel {
    func some1Function() {}
    
    func some2Function(value: Int) {}
    
    func some3Function(value: String) {}
}

途端に、以下の様な課題が発生してしまうと思います。

  • 可読性の低下
  • ChildViewのプロパティの増加

上記の例はまだクロージャ自体がシンプルなため比較的問題は軽微ですが、クロージャの行数が多くなると可読性の問題は益々悪化していくことが予想出来ます。

打ち手

この課題をenumで管理することによって解決したいと思います。
まずはChildView側の実装です。
以下のようにenumを作成し、enumでactionを管理するようにします。

ChildView.swift
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を以下のように実装します。

ParentView.swift
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で管理していきたいと思いました。
機会があれば、是非ご自身のプロジェクトでもこの実装を試して頂けると幸いです。

最後までお読み頂き、ありがとうございました。

参考

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2