質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2179閲覧

【SwiftUI】MVVMにおける値の連携方法(@Observed Objectなど)

ishiishiyay

総合スコア33

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2021/12/10 06:07

ViewModelでユーザー情報を取得するAPIを呼び、Modelに格納し、Viewに連携してあげたいです。
1つ目のやり方は成功したものの、コードを綺麗にするために2つ目のやり方を試したら失敗しました。
ご助言いただけますと幸いです。(MVVMアーキテクチャにおいて何かタブーなことをやっているのかもしれません。。)
何卒よろしくお願いいたします。

成功したコード

ViewModel

1class AuthViewModel: ObservableObject { 2 3@Published var userMe: User? //UserはユーザーIDや名前など諸々の情報が入るModelのStructです。 4 5init() {//アプリが開かれた瞬間にinitされる 6 fetchUserMe() 7 } 8 9 func fetchUserMe() { 10 //API通信 中略 11 self.userMe = try JSONDecoder().decode(User.self, from: data) 12} 13}

View

1//このViewは、NavigationLinkで遷移する先となる子Viewです。親Viewは省略させていただきます。 2struct ProfileHeaderView: View { 3 @EnvironmentObject var viewModel: AuthViewModel 4 5var body: some View { 6 HStack { 7 Text( viewModel.userMe?.usersName ?? "nil") 8} 9}

View

1@main 2struct FriendQA_SwiftUIApp: App { 3 4 @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate 5 var body: some Scene { 6 WindowGroup { 7 ContentView().environmentObject(AuthViewModel.shared) 8 } 9 } 10}

エラーになったコード

上記でうまくいくものの、AuthViewModelには既にたくさんの関数を書いてしまってごちゃごちゃしているので、
fetchUserMe関数を違うView Modelに移したいと思い、下記のコードに変えましたが、うまくいきませんでした。

ViewModel

1class AuthViewModel: ObservableObject { 2 3@ObservedObject var fetchUsersViewModel = FetchUsersViewModel() 4 5init() { //アプリが開かれた瞬間にinitされる 6 fetchUsersViewModel.fetchUserMe() 7 } 8}

ViewModel

1class FetchUsersViewModel: ObservableObject { 2 3@Published var userMe: User? 4 5 func fetchUserMe() { 6 //API通信 中略 7 self.userMe = try JSONDecoder().decode(User.self, from: data) 8 print(self.userMe) //ちゃんと中身も出力される 9} 10}

View

1struct ProfileHeaderView: View { 2 @ObservedObject var fetchUsersViewModel = FetchUsersViewModel() 3 4var body: some View { 5 HStack { 6 Text(fetchUsersViewModel.userMe?.usersName ?? "nil") //nilになってしまう 7 Text($fetchUsersViewModel.userMe.wrappedValue?.usersName ?? "nil") //nilになってしまう 8} 9}

最後まで読んでいただきありがとうございます。お手数ですが、お力添えいただけますと幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2021/12/10 06:49

同じクラスの異なるインスタンスを作ってるからでは。
ishiishiyay

2021/12/10 07:06

Hoshi-takanoriさん、いつもありがとうございます。 同じクラスの異なるインスタンスはここでしょうか??: @ObservedObject var fetchUsersViewModel = FetchUsersViewModel() ちなみに、Profile Header Viewにinit()を記載しその中でfetchUsersViewModelのfetchUserMe()を呼び出したらうまく行ったのですが、Profile Header Viewを表示してからViewが更新されるまでタイムラグがあるので、出来ればアプリ起動と同時にAPI通信したく、 成功した一番最初の書き方しかないでしょうか...?
退会済みユーザー

退会済みユーザー

2021/12/11 10:07

横から書き込みしてしまってすみません・・ hoshi-takanori さんのおっしゃる通り、Viewから参照しているのは直接的な`FetchUsersViewModel`のようです。 `AuthViewModel`の`FetchUsersViewModel`を参照している訳ではなさそうです。 > 同じクラスの異なるインスタンスはここでしょうか??: > @ObservedObject var fetchUsersViewModel = FetchUsersViewModel() そういうことだと思います。 `AuthViewModel`のfetchUsersViewModelを参照しないことには、思った通りにはならないように思います。 > 成功した一番最初の書き方しかないでしょうか...? そんなことはないとは思いますが、 文字で説明するのが大変になってきそうです・・ クラスの構造や処理の流れを整理してご自身で一つずつ理解するところから始めた方が良さそうです。 --- > (MVVMアーキテクチャにおいて何かタブーなことをやっているのかもしれません。。) 私自身、MVVMの理解が浅いのですが、 かつ、SwiftでのMVVMは、業務レベルでは皆無ですが、 ModelでやるべきことをViewModelでやってしまっているのではないか、 という気もします。 > ViewとViewModelでプレゼンテーション・ロジックを分担します。ViewModelはUI要素と結合しないプレゼンテーション・ロジックを担当し、ViewはUI要素に結合したプレゼンテーション・ロジックを担当します(具体的にはSystem.Windows名前空間ではじまる列挙体・構造体以外の型のプロパティはViewModelでは所持しないなどの切り分けが可能です)。 > > https://atmarkit.itmedia.co.jp/fdotnet/chushin/greatblogentry_02/greatblogentry_02_03.html . > MVVMにおいてはViewModel同士がお互いを操作する必要なんて基本的にはありません。所有関係はあることはありますがそもそもViewModelはModelの影なのです。そしてまたViewはViewModelの影でもあります。 > > https://ugaya40.hateblo.jp/entry/model-mistake (MVVMの発祥がC#な気がしておりますので、Swiftに関する質問ですが、あえてC#の記事を引用させていただきました)
ishiishiyay

2021/12/11 16:01

xg63ex2bさん、ご詳細に誠にありがとうございます!ViewModel同士がお互いを操作する必要ないんですね。関数をコールするだけならいいかなと思ってしまいました。設計の方法から勉強し直します!ありがとうございます。
guest

回答1

0

ベストアンサー

onAppear()でうまくいくかもしれません。

swift

1 2class FetchUsersViewModel: ObservableObject { 3 @Published var userMe: User? 4 5 func fetchUserMe() { 6 self.userMe = try JSONDecoder().decode(User.self, from: data) 7 print(self.userMe) //ちゃんと中身も出力される 8 } 9}

swift

1struct ProfileHeaderView: View { 2 @ObservedObject var fetchUsersViewModel = FetchUsersViewModel() 3 4 var body: some View { 5 HStack { 6 Text(fetchUsersViewModel.userMe?.usersName ?? "nil") 7 Text($fetchUsersViewModel.userMe.wrappedValue?.usersName ?? "nil") 8 } 9 .onAppear { 10 fetchUsersViewModel.fetchUserMe() 11 } 12 } 13}

投稿2021/12/13 05:35

zundaa

総合スコア101

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問