0

I want to have a swift func() that takes a documentID and returns the corresponding document. I want this func() to use async/await and provide realtime updates whenever the document is modified by others.

The following document snippet from firestore doco, works fine for me when I have the code hard coded within the .task {} area of my view but I want it to be called as a func() that resides in my viewmodel.

Here is the code snippet from Firebase doco.

db.collection("cities").document("SF")
    .addSnapshotListener { documentSnapshot, error in
      guard let document = documentSnapshot else {
        print("Error fetching document: \(error!)")
        return
      }
      guard let data = document.data() else {
        print("Document data was empty.")
        return
      }
      print("Current data: \(data)")
    }

But here is what I'm trying to do... I'm getting loads of errors with this.

   //
    // example: fetching a single object with a listener
    //
func fetchCityWithListener(documentID: String) async throws -> City {
    
    db.collection("cities").document(documentID)
        .addSnapshotListener { documentSnapshot, error in
            guard let document = documentSnapshot else {
                print("Error fetching document: \(error!)")
                return
            }
            guard let data = document.data() else {
                print("Document data was empty.")
                return
            }
            print("Current data: \(data)")
            let city = try? Firestore.Decoder().decode(City.self, from: data)
            return city
        }
}

My intent is to call the function like this...

   // example: call to get a single object with a listener on it
    //
    Task {
        do {
            city = try await cityViewViewModel.fetchCityWithListener(cityID: "atlanta")
            // Handle the fetched city
            
        } catch {
            // Handle the error
            print("Error: \(error)   - skljle88")
        }
    }
phil
  • 993
  • 1
  • 10
  • 34
  • 1
    You are mixing old completion handlers with the new Concurrency and mixing asynchronous with synchronous. Stick with one version. – lorem ipsum Jul 13 '23 at 21:00
  • Does this answer your question? [Thread 1: EXC\_BAD\_INSTRUCTION when fetching data](https://stackoverflow.com/questions/73636543/thread-1-exc-bad-instruction-when-fetching-data) – lorem ipsum Jul 13 '23 at 21:01
  • https://stackoverflow.com/questions/76569481/how-to-use-swifts-asyncthrowingstream-with-firestore-listeners/76573528#76573528 – lorem ipsum Jul 13 '23 at 21:02
  • The first link is likely what you are looking for but a listener is actually more of a stream – lorem ipsum Jul 13 '23 at 21:03
  • thank you, lorem ipsum. The two examples you provide both return an array of objects with an associated listener. I am trying to have a function return a single object with a listener. The sample they provide in firebase doco is not encoded as a func - just inline code. Seems like it would be a simple conversion to the body of a func(). I'm still digging into your examples. Thank you. – phil Jul 13 '23 at 22:24
  • Firebase has proper async await methods. Just beware of mixing methodologies. – lorem ipsum Jul 13 '23 at 22:27
  • 1
    I think I understand the question... but... the issue is that `await` expects data once and only once. So it would not make sense to add a listener to a document (which will change over time) and expect the returned value to also change; that would be handled by a function closure. `await` and `addSnapshotListener` are two totally different processes. In this case the best bet is to retrieve the city with a `addSnapshotListener` and its closure and then handle the fetched city within that closure - you can use an escaping clause to make it feel more synchronous if needed. – Jay Jul 15 '23 at 14:53
  • Thank you, Jay! Would have thanked you sooner but had a big family wedding this past weekend! – phil Jul 20 '23 at 02:28

0 Answers0