71299


<https://developer.apple.com/tutorials/swiftui/handling-user-input>  
こちらのチュートリアルを実施していて疑問ができたので質問します。

【背景】  
以下のようにObservableObjectに準拠するクラスを作成します

  * UserData.swift

[code]

    import Foundation
    import Combine
    
    final class UserData: ObservableObject  {
        @Published var showFavoritesOnly = false
        @Published var landmarks = landmarkData
    }
    
[/code]

それを利用するViewを二つ作成します。

  * LandmarkList.swift

[code]

    import SwiftUI
    
    struct LandmarkList: View {
        @EnvironmentObject var userData: UserData
        
        var body: some View {
            NavigationView {
                List {
                    Toggle(isOn: $userData.showFavoritesOnly) {
                        Text("Favorites only")
                    }
                    ...
                }
                .navigationBarTitle(Text("Landmarks"))
            }
        }
    }
    ...
    
[/code]

  * LandmarkDetail.swift

[code]

    import SwiftUI
    
    struct LandmarkDetail: View {
        @EnvironmentObject var userData: UserData
        var landmark: Landmark
        
        var landmarkIndex: Int {
            userData.landmarks.firstIndex(where: { $0.id == landmark.id })!
        }
        
        var body: some View {
            VStack {
                ...
                VStack(alignment: .leading) {
                    HStack {
                        ...
                        Button(action: {
                            self.userData.landmarks[self.landmarkIndex].isFavorite.toggle()
                        }) {
                            if self.userData.landmarks[self.landmarkIndex].isFavorite {
                                Image(systemName: "star.fill")
                                    .foregroundColor(Color.yellow)
                            } else {
                                Image(systemName: "star")
                                    .foregroundColor(Color.gray)
                            }
                        }
                    }
        ...            
        }
    }
    ...
    
    
[/code]

このようにした際にLandmarkDetailのButtonでisFavoriteを更新して戻ると、LandmarkListの方でも更新が反映されています。

【お聞きしたいこと】

  * この挙動を見るとLandmarkListとLandmarkDetailで参照しているuserDataが同一インスタンスに見えるのですが、その認識で合っていますか？
  * 別Viewで参照したものが同インスタンスなのは気持ち悪いので、@BindingでLandmarkListからLandmarkDetailにObservableObjectに準拠させたLandmarkを渡す方が自然な気がするのですが、このような実装(上記のチュートリアルのような実装)はよくあるのでしょうか？ Appleのチュートリアルがこれなのでそういうものなのかな？と思うのですが。。

不勉強なためそもそも勘違いの気もするので、間違いがあったら修正いたします。  
以上よろしくお願いいたします。


CC BY-SA 4.0
2020-10-18T03:27:47.633
71295
2020-10-19T00:12:42.233
2020-10-19T00:12:42.233
2238
24870
question
swift swiftui
ObservableObjectに準拠しているクラスは複数Viewで参照が共有されるのでしょうか？


>
> この挙動を見るとLandmarkListとLandmarkDetailで参照しているuserDataが同一インスタンスに見えるのですが、その認識で合っていますか？

はい。

> 別Viewで参照したものが同インスタンスなのは気持ち悪い

「気持ち悪い」と言う極度に主観的な表現に対しては、お答えできる内容はありません。例えばどのような弊害がありうると思われているのか、もう少し技術的な観点から疑念・疑問などを明確化していただければ何かお答えできることがあるかもしれません。

> @BindでLandmarkListからLandmarkDetailにObservableObjectに準拠させたLandmarkを渡す

「@Bind」と言うのは、`@Binding`のことでしょうか？だとしたら、参照型のインスタンスを`@Binding`で修飾された変数に持つというのが意味を持つことは殆どありません。全くもって不自然な記述です。「参照型」と「インスタンスの生成」について、十分理解出来ていないのではないかと思います。

* * *

> ObservableObjectに準拠しているクラスは複数Viewで参照が共有されるのでしょうか？

「`ObservableObject`に準拠している」かどうかには全く関わりなく、(参照型である)クラスのインスタンスを保持する変数は、同じインスタンスが渡されていれば共有されますし、別のインスタンスを生成して渡せば共有はされていないと言うことになります。

`@EnvironmentObject`と言うのは、いわば「同一のインスタンスを共有しやすくするための仕組み」であり、リンク先のチュートリアルで言えば、SceneDelegate.swift
の17行目、

[code]

    .environmentObject(UserData())
    
[/code]

ここで生成された`UserData`のインスタンスを`LandmarkList`配下のViewで共有してもらうために、`environmentObject`を呼んでいます。

* * *

この辺はSwiftUIについては、典型的なパターンですが、

  * 画面に反映させたい状態をプロパティに持つクラスを`ObservableObject`に準拠させる
  * それらのプロパティには`@Published`を指定する
  * それらのプロパティでUIを更新したいViewは`@ObservedObject`(または`@StateObject`か`@EnvironmentObject`を指定して、そのクラスのインスタンスを保持するプロパティを保持する

と言う手順を取ります。「`ObservableObject`に準拠させる」と言うのは、同一インスタンスを共有するための仕組みではなく、「クラスのプロパティの変化を画面に反映させる」ための仕組みと言えます。

* * *

何かわかりにくい点があれば、コメント等していただけるようお願いします。


CC BY-SA 4.0
2020-10-18T06:25:45.333
71299
2020-10-18T06:38:23.963
2020-10-18T06:38:23.963
13972
13972
71295
answer