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

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

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

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

Q&A

解決済

2回答

457閲覧

Firestoreのデータの取得で'self' used in property access 'model' before all stored properties are initialized

avocadoningen

総合スコア14

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

0グッド

0クリップ

投稿2022/09/04 02:51

前提

XcodeでFirestoreのデータを取得しようとしています。
ですが、実行するとLocationsViewModel.swiftのlet locations = model.storesに
'self' used in property access 'model' before all stored properties are initialized
とエラーが現れます。
プロジェクトとFirebaseは連携できていることは確認できています。
回答していただけると大変ありがたいです。
どうぞ、よろしくお願いいたします。

実現したいこと

エラーを解決し、Firestoreのデータをアプリに反映させる。

発生している問題・エラーメッセージ

LocationsViewModel.swift1行目のlet locations = model.stores

'self' used in property access 'model' before all stored properties are initialized

該当のソースコード

LocationsViewModel.swift

1class LocationsViewModel: ObservableObject { 2 @ObservedObject var model = FirebaseViewModel() 3 4 init() { 5 let locations = model.stores 6 self.locations = locations 7 self.mapLocation = locations.first! 8 self.updateMapRegion(location: locations.first!) 9 } 10}

Firestoreからデータを取得するコード

FirebaseViewModel.swift

1import SwiftUI 2import FirebaseFirestore 3import MapKit 4 5class FirebaseViewModel: ObservableObject { 6 @Published var stores = [Location]() 7 8 init() { 9 self.stores = stores 10 getData() 11 } 12 13 14 func getData() { 15 16 let db = Firestore.firestore() 17 18 db.collection("stores").getDocuments { snapshot, error in 19 if error == nil { 20 // No errors 21 if let snapshot = snapshot { 22 DispatchQueue.main.async { 23 self.stores = snapshot.documents.map { d in 24 return Location(name: d["name"] as? String ?? "", 25 category: d["category"] as? String ?? "", 26 link: d["url"] as? String ?? "", 27 description: d["description"] as? String ?? "", 28 coordinates: d["coordinates"] as! GeoPoint, 29 id: d.documentID) 30 } 31 } 32 } 33 } 34 else { 35 } 36 } 37 38 } 39} 40
### 補足情報(FW/ツールのバージョンなど) macOS バージョン12.4 1.1GHz 4コアIntel Core i5 Xcodeバージョン13.4.1

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

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

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

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

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

guest

回答2

0

エラーメッセージの通りですが、
エラーの意味は、
LocationsViewModelのインスタンスが完全に生成されるまでは
そのインスタンスのプロパティを参照することはできません
ということだと思います。

イニシャライザ(init())でインスタンスを生成することになりますので、
(イニシャライザが終わるまではインスタンスはまだ完全に生成されていないことになります)
この処理の中でインスタンスのプロパティ(model)を参照していてエラーになっていると思います。

影響がわからないですが、次のような感じはいかがでしょうか。

swift

1 @ObservedObject var model: FirebaseViewModel // ← プロパティの初期値は設定しないでイニシャライザで設定します。 2 init() { 3 // イニシャライザでプロパティを設定します(modelプロパティも含めて)。 4 let initmodel = FirebaseViewModel() 5 self.model = initmodel 6 let locations = initmodel.stores 7 self.locations = locations 8 self.mapLocation = locations.first! 9 self.updateMapRegion(location: locations.first!) 10 }

イニシャライザは奥が深いと感じますので、ドキュメントも読み込むと良いかもしれませんね。

Initialization — The Swift Programming Language (Swift 5.7)


それから気になったのはObservableObjectについてです。

View -> LocationsViewModel -> FirebaseViewModel
のようにクラスの参照があるのかなと思いますが、
FirebaseViewModel.storesのプロパティが変わったとしても
Viewはちゃんと変更を受け取れないように思いました。
*内部の値が変わったとしても画面にその内容が表示されないと思います

Viewが変更を受け取れるのは
LocationsViewModelの中に@Publishedプロパティがあったとして
そのプロパティ自体が変わった場合のみだと思います。

ちょっと今回の質問とは別の問題になると思いますので、
必要であればまた別に質問していただければと思います。

追記です。

コメントありがとうございます。

不明な点がありましたら
解決済みにせずに回答者に確認しても良いかもしれませんね。

エラーは解消されましたが、getData()が呼び出されていないみたいです、、、

おそらく getData() は呼び出されているのではないかと思っています。
問題を正しく認識しないと対処もできないと思いますので、
問題になっていそうなポイントをサンプルのコードとして書いてみました。
タップするとindex(カウンター)が1ずつ増えていくだけのものです。

でもこれは動きません。
View -> LocationsViewModel -> FirebaseViewModel
という参照があって、
FirebaseViewModelの内容を変更しているためです。
Viewが変更を受け取れるのはLocationsViewModelの中のプロパティ”自身”だけです。
LocationsViewModelのmodelプロパティの”中身”(FirebaseViewModelの中のプロパティ)が変わってもViewは変更を受け取れないのです・・

どう対処するのが良いのかは私自身まだ理解できていませんので、
勉強しておきたいと思います・・

swift

1import SwiftUI 2 3struct ContentView: View { 4 @ObservedObject var vm = LocationsViewModel() 5 var body: some View { 6 Text("Hello, world!") 7 .padding() 8 Text("index: \(vm.model.index)") 9 Button("tap", action: action) 10 } 11 func action() { 12 // indexを変更してもViewには反映されません。 13 vm.model.index += 1 14 print("index: \(vm.model.index)") 15 // model自体が変わるのであればViewは変更を受け取れます。 16 // 例えば強引ですが...modelをmodelに再代入するとViewは変更を受け取れます... 17 // vm.model = vm.model 18 } 19} 20 21class LocationsViewModel: ObservableObject { 22 @Published var model: FirebaseViewModel 23 init() { 24 let initmodel = FirebaseViewModel() 25 self.model = initmodel 26 } 27} 28 29class FirebaseViewModel: ObservableObject { 30 @Published var index = 0 31 init() { 32 getData() 33 } 34 func getData() { 35 print("getData") 36 DispatchQueue.main.async { 37 // indexを変更してもViewには反映されません。 38 self.index = 10 39 print("index(getData): \(self.index)") 40 } 41 } 42}

投稿2022/09/04 04:21

編集2022/09/08 00:27
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

avocadoningen

2022/09/07 23:07

丁寧にありがとうございます。 エラーは解消されましたが、getData()が呼び出されていないみたいです、、、 頑張って呼び出します。
avocadoningen

2022/09/08 06:16

ですがprint文をgetData()の中に書くと、表示されませんでした。 心当たりありませんか?
退会済みユーザー

退会済みユーザー

2022/09/08 06:21

コメントありがとうございます。 > ですがprint文をgetData()の中に書くと、表示されませんでした。 そうだったのですね。 FirebaseViewModelのinit()の中にprintを記述すると出力されますでしょうか? init()の中でgetDataを呼び出していますよね? 手元の環境ではgetData()自体は呼び出されているのですけどね。
avocadoningen

2022/09/10 04:13

View同士の関係がよくわからなかったので、すべてLocationsViewModelに入れるとうまくいくようになりました!親切にありがとうございました!!
退会済みユーザー

退会済みユーザー

2022/09/10 06:19

コメントありがとうございます。 すべてLocationsViewModelに入れることにしたのですね。 先に進めたようで何よりです。
guest

0

自己解決

すべてをFirebaseViewModelの中身をLocationsViewModelに入れると解決しました

import Foundation import MapKit import SwiftUI import FirebaseFirestore class LocationsViewModel: ObservableObject { @Published var locations = [Location]() init() { getData() self.locations = locations } func getData() { let db = Firestore.firestore() db.collection("stores").getDocuments { snapshot, error in if error == nil { // No errors if let snapshot = snapshot { DispatchQueue.main.async { self.locations = snapshot.documents.map { d in return Location(name: d["name"] as? String ?? "", category: d["category"] as? String ?? "", link: d["url"] as? String ?? "", description: d["description"] as? String ?? "", latitude: d["latitude"] as! Double, longitude: d["longitude"] as! Double, id: d.documentID) } } } } else { //Handle the error print("there is an error with gettiing codes from Firebase") } } } }

投稿2022/09/10 04:16

avocadoningen

総合スコア14

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問