👌

「SwiftUIでMVVMやるシンプルなパターン3つの違い」が分からなかったので、初学者が調べながら説明してみる。

2023/05/21に公開

https://zenn.dev/kyome/articles/710cde86537d45
こちらの記事を参考にしました。大変勉強になりました。
初学者のため、一部理解しきれていない部分があったので、調べてみる。

上記記事のまとめ

1つ目のやり方


2つ目のやり方


3つ目のやり方

初学者は違いが分からなかった。

ポイント

ポイントとしては、

  • 子孫Viewの使い回しができるかどうか(ViewがViewModelに依存しているかどうか)
  • バケツリレーをしなければならないか、どうか(EnvironmentObjectを用いるかどうか)
    挙げられる。

なぜそういったことが生じてしまうのか。一つひとつ深ぼっていく。

子孫Viewの使い回しができるかどうか(ViewがViewModelに依存しているかどうか)

1,2 のSubViewのサンプルコードを見てみよう。

1のサンプルコード
struct SampleSubView: View {
    @ObservedObject var viewModel: SampleViewModel
    
    init(viewModel: SampleViewModel) {
        self.viewModel = viewModel
    }
    
    var body: some View {
        VStack(spacing: 8) {
            Text(viewModel.stars)
            Button("Change Stars Length") {
                viewModel.changeStarsLength(Int.random(in: 1 ..< 10))
            }
        }
    }
}
2のサンプルコード
struct SampleSubView: View {
    @Binding private var stars: String
    private var handler: (Int) -> Void
    
    init(stars: Binding<String>, action handler: @escaping (Int) -> Void) {
        self._stars = stars
        self.handler = handler  // クロージャーを保持
    }
    
    var body: some View {
        VStack(spacing: 8) {
            Text(stars)
            Button("Change Stars Length") {
                handler(Int.random(in: 1 ..< 10))
            }
        }
    }
}

初期値として設定すべき、プロパティの型を見て頂きたい。
init(...) {...} の中身を見るだけでも良いです。

1の場合は、SampleViewModelを設定
2の場合は、Binding<String> (Int) -> Voidを設定

このようなViewの設定が必要になってくる。
1では、SampleViewModel という型を用いた実装でしか、このViewを用いることができません。
だが、
2では、String型引数がInt型である関数を初期値に代入することができれば、どこの画面でも用いることができます。

例えば、SampleSubViewで、お気に入りボタン♡ を実装していたとしよう。
そのお気に入りボタンの実装を、画面1でも使いたいし、画面3、画面4でも使いたくなった場合には、例2の方法の方が望ましい。例1はSampleViewModelに依存しているので、他の画面でもSampleViewModelを用いる必要があり、望ましくない実装になってします。
これらの状態が依存している状態であると、筆者は解釈しました。

バケツリレーをしなければならないか、どうか(EnvironmentObjectを用いるかどうか)

@ObserverdObjectとの違いは、バケツリレーのように子Viewに引き渡す必要がありません。インスタンスを階層トップのViewに紐付けると、アプリケーション全体からアクセス可能になります。
アプリケーション全体で共有するようなデータを扱う用途で使用します。

→階層のトップのViewに紐付けると、どこでもEnvironmentObjectを用いて呼び出すことができるよということでした。ObserverObjectであれば、わざわざ子Viewに値を渡す必要があるため、それが孫Viewや、ひ孫View、玄孫View、来孫View...と渡していかなければなりません。その問題に対して、EnvironmentObjectは、バケツリレーせずに、来孫Viewに値を渡すことができます。便利ですよね。
祖父母Viewには渡せないので、ご注意を。

参考
https://capibara1969.com/3119/


他にも良い方法があれば、コメントいただけると大変うれしいです。
良かったと思ったら、いいねやTwitterのフォローよろしくお願いいたします!

https://sites.google.com/view/muranakar
個人でアプリを作成しているので、良かったら覗いてみてください!

Discussion