Benjamin B.
Benjamin B.

Reputation: 793

Firestore in Swift - Listener producing needless reads

I have an app that uses a snapshot listener to listen to data in a particular document. However, when a field in the document is updated, the data is read 7-10x over. Never read once, and never read the number of fields that are in my document, it always seems to be an arbitrary number. Also, when the read data prints out, it seems like every printout is the same except for a couple of fields that I'm not setting (like an array prints out "<__NSArrayM 0x282d9f240>" but the number changes on each print). As a result, minimal usage of my app is causing 5-10k reads. I'm trying to reduce the number of reads and I don't know exactly how, but the app has to read as data is updated, but my two questions are:

  1. when I print the data from the listener, does each data print out signify a separate read operation? and

  2. is there any way for the listener to be alerted of the update but wait to actually perform the read until the data is updated, then perform one read instead of multiple reads every time any field is updated? Or another strategy to reduce reads when multiple writes occur?

Not sure if this is helpful, but here is the code I'm using to perform the read...its pretty much the standard code from the firestore sdk:

 env.db.collection(env.currentSessionCode!).document(K.FStore.docName).addSnapshotListener { [self] documentSnapshot, error in
            guard let document = documentSnapshot else {
                print("Error fetching snapshot: \(error!)")
                return
            }
            guard let data = document.data() else {
                print("Document data was empty.")
                return
            }
                        

            self.env.data1 = data[K.FStore.data1] as? String ?? "????"
            self.env.data2 = data[K.FStore.data2] as? String ?? "????"
            self.env.data3 = data[K.FStore.data3] as? [String] ?? ["????"]
            self.env.data4 = data[K.FStore.data4] as? [String] ?? ["????"]
            self.env.data5 = data[K.FStore.data5] as? Double ?? 0
            self.env.data6 = data[K.FStore.data6] as? Double ?? 0
            self.env.data7 = data[K.FStore.data7] as! Bool
            self.env.data8 = data[K.FStore.data8] as! Bool
            
            print("Current data: \(data)")
            

Update - For clarification, the way I have been updating my data to firebase is with a environment object, and using "didSet" when the new data is changed/updated in the environment to update it on firebase...I think this might be the root of the problem, as the function called on didSet runs 4-5 times each time it is called...

relevant code:

@Published var data1: String {
        didSet {
            postValuesToFB(fb: K.FStore.data1, string: data1)
        }
    }

    func postValuesToFB(fb: String, string: String) {
        guard let code = currentSessionCode else {
            fatalError("Error - Connection Check - no value for current session code in Global Env")
        }
        
        let docRef = db.collection(code).document(K.FStore.docName)
        docRef.getDocument { document, _ in
            guard let document = document else {
                return
            }
            if document.exists {
                let session = self.db.collection(code).document(K.FStore.docName)

                session.updateData([
                    fb: string,
                    K.FStore.dateLastAccessed: FieldValue.serverTimestamp(),
                ])

                return
            }
        }
    }

Upvotes: 0

Views: 721

Answers (2)

Doug Stevenson
Doug Stevenson

Reputation: 317467

Based on your comments, it sounds as if you've written no code to remove a listener after it's been added. Based on this, it's relatively safe to assume that your code could be adding many listeners over time, and each one is getting called for each change.

You should take a moment to think about the architecture of your app and figure out when is the appropriate time to remove listeners when they're no longer needed. Usually this corresponds with the lifecycle of whatever component is responsible for display of the data from the query. Review the documentation for getting realtime updates, especially the section on detaching a listener. It's up to you to determine the right time to remove your listener, but you definitely don't want to "leak" a listener as you are now.

Upvotes: 2

Frank van Puffelen
Frank van Puffelen

Reputation: 598837

A common source of unexpected read charges for developers who are new to Firestore is the Firebase console itself. When that console displays Firestore content, you are charged for those read too. To ensure you measure the impact of your code correctly, test it with the Firebase console closed.

when I print the data from the listener, does each data print out signify a separate read operation?

Not really. You get charged for a document read, when the document is read on your behalf on the server. You are not charted for printing the same DocumentSnapshot multiple times.

is there any way for the listener to be alerted of the update but wait to actually perform the read until the data is updated

Nope. To know the document has changed, the server needs to read it. So that requires a charged read operation.

Upvotes: 1

Related Questions