1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

えーえすAdvent Calendar 2024

Day 17

Firestoreでドキュメント内のコレクションを監視する

Last updated at Posted at 2024-12-16

はじめに

FirestoreではCodableに準拠したstructを用いることで簡単にデータの読み書きを行うことができます。また、Firestore上での変更を監視することも可能です。

しかし、structの配列を持つ場合、簡単に監視をすることができません。

以前ここですごく詰まったので、記事にまとめます。

現状の構造

UserScoreの配列を持つものとします。

struct User: Codable {
    @DocumentID var id: String?
    var name: String
    var email: String
    var scores: [Score]
}

struct Score: Codable {
    @DocumentID var id: String?
    var score: Int
}

また、監視のためのaddSnapshotListenerをこのように書いているとします。

listener = db.collection("users").addSnapshotListener { snapshot, error in
    self.users = snapshot!.documents.compactMap { document in
        try? document.data(as: User.self)
    }
}

Firestore上の構成はこのようになっているとします。

スクリーンショット 2024-12-16 18.09.12.png

しかし、このように取得すると取得ができず、エラーになってしまいます。

原因

documentの中にあるcollectionは、Codableの配列として扱われません!

もしscoresusersの監視で取得したければ、以下のような構成にしなければなりません。

スクリーンショット 2024-12-16 18.10.47.png

しかしこれだと、documentの中にcollectionが作れる良さが薄れますね…

改善案

ということで、usersのaddSnapshotListenerの時に、各scoresにもaddSnapshotListenerをしましょう。

struct User: Codable, Identifiable {
    @DocumentID var id: String?
    var name: String
    var email: String
    var scores: [Score] = [] //こうすることでinit時にエラーが起こらない
}

struct Score: Codable {
    @DocumentID var id: String?
    var score: Int
}

class UsersViewModel: ObservableObject {
    @Published var users: [User] = []
    private var db = Firestore.firestore()
    private var userListeners: [ListenerRegistration] = []
    private var mainListener: ListenerRegistration?
    
    init() {
        fetchUsers()
    }
    
    func fetchUsers() {
        mainListener = db.collection("users").addSnapshotListener { snapshot, error in
            if let error = error {
                print("Error fetching users: \(error.localizedDescription)")
                return
            }
            
            guard let documents = snapshot?.documents else {
                print("No documents found")
                return
            }
            
            self.users = documents.compactMap { document in
                try? document.data(as: User.self)
            }
            
            self.removeUserListeners()
            for user in self.users {
                if let userID = user.id {
                    self.fetchScores(for: userID)
                }
            }
        }
    }
    
    func fetchScores(for userID: String) {
        let listener = db.collection("users").document(userID).collection("scores").addSnapshotListener { snapshot, error in
            if let error = error {
                print("Error fetching scores for user \(userID): \(error.localizedDescription)")
                return
            }
            
            guard let documents = snapshot?.documents else {
                print("No scores found for user \(userID)")
                return
            }
            
            let scores = documents.compactMap { document in
                try? document.data(as: Score.self)
            }
            
            DispatchQueue.main.async {
                if let index = self.users.firstIndex(where: { $0.id == userID }) {
                    self.users[index].scores = scores
                }
            }
        }
        userListeners.append(listener)
    }
    
    private func removeUserListeners() {
        for listener in userListeners {
            listener.remove()
        }
        userListeners.removeAll()
    }
    
    deinit {
        mainListener?.remove()
        removeUserListeners()
    }
}
1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?