How retrieve values from an object on Cloud Firestore? Swift

14,364

Solution 1

Your readArray code is SUPER close. Just need to add code to read the individual fields within the document

func readArray()
   self.db.collection("places").getDocuments { (snapshot, err) in
       if let err = err {
           print("Error getting documents: \(err)")
       } else {
           for document in snapshot!.documents {
              let docId = document.documentID
              let latMax = document.get("latMax") as! String
              let latMin = document.get("latMin") as! String
              let lonMax = document.get("lonMax") as! String
              let lonMin = document.get("lonMin") as! String
              print(docId, latMax, latMin, lonMax, lonMin)
           }
       }
}

The problem in the original question is the structure of the database. Here's a structure that will match the code in my answer and will be better suited for what the OP wants to accomplish.

The structure in the question has multiple places listed under one collection - that's OK but it groups the places together. If we change that to have one place per collection, it makes iterating over them and getting to the data much easier.

enter image description here

Solution 2

I think your places model have references to two objects bestBuy and house. So, the retrieval approach would be to retrieve the data from and store in the object only. Means you can directly set all the data in plcaes model. Then you can call getter methods of bestBuy and house to retrieve the data as you want. Here is a sampe code (here it is retrieving only one document) but you can apply the same for all documents by iterating a for loop and converting each document to object:-

let docRef = db.collection("cities").document("BJ")

docRef.getDocument { (document, error) in
    if let city = document.flatMap({
      $0.data().flatMap({ (data) in
        return City(dictionary: data)
      })
    }) {
        print("City: \(city)")
    } else {
        print("Document does not exist")
    }
}

But according to firestore documentation the best way to store nested objects is creating a subcollection and then storing it in its document.

Solution 3

Some clarification since the iOS Firestore documentation is, IMO, light in many areas: there are two ways to retrieve the values from a document field (once the document has been gotten)--using a Firestore method (per the documentation) and using Swift's native method of getting values from dictionaries.

let query = Firestore.firestore().collection("zipCodes").whereField("california", arrayContains: 90210)

query.getDocuments { (snapshot, error) in

    guard let snapshot = snapshot,
        error == nil else {
            return print("error: \(error!)")
    }
    guard !snapshot.isEmpty else {
        return print("results: 0")
    }

    for doc in snapshot.documents {

        // using firestore's convention
        if let array = doc.get("zipCodes") as? [Int] {
            if array.contains(90211) {
                // doc contains both zip codes
            }
        }

        // using swift's convention
        if let array = doc.data()["zipCodes"] as? [Int] {
            if array.contains(90211) {
                // doc contains both zip codes
            }
        }

    }

}

Personally, I would use as much of Firestore's framework as possible for safety and predictability and, therefore, use get() for all field retrieval.

Share:
14,364
Robby
Author by

Robby

Updated on June 07, 2022

Comments

  • Robby
    Robby about 2 years

    I'm quite new on Firestore and Firebase libraries. Can someone explain how could I retrieve the fields from my object and stored in a variable on my code? I'm trying to access all the latMin, latMax, lonMin, and lonMax from each object in my Firestore, as well if is a way to retrieve each field with the same name (ex. latMin from place 1 and 2). I don't know if this is possible or maybe there is a better way to have organized my Firebase.

    Here is my Cloud Firebase: Here is the image for my Firestore, requested in the comments.

    place 1:  //This is an Object in my Firestore
    
      //Field = Number : Value ----> This is how object is orginazed 
    
      latMax : 39.727
      latMin : 39.726
      lonMax : -121.7997
      lonMin : -121.8003
    
    place 2: 
    
      latMax : 39.7559
      latMin : 39.755
      lonMax : -122.1988
      lonMin : -122.1992
    

    I was able to access the objects without any problem, this is the function I'm using to read the documents on my Firestore:

    import UIKit
    import FirebaseFirestore
    import Firebase
    import CoreLocation
    
    class SecondViewController: UIViewController, CLLocationManagerDelegate {
    
    //Creating access to locationManager
    var locationManager : CLLocationManager!
    
    @IBOutlet weak var latLabel: UILabel!
    @IBOutlet weak var lonLabel: UILabel!
    @IBOutlet weak var place: UILabel!
    @IBOutlet weak var placeImage: UIImageView!
    
    
    //Storing the pass data that we got from the firt View
    var lonStore = String()
    var latStore = String()
    var fireLon = String()
    var fireLat = String()
    var lonNumberStore = Double()
    var latNumberStore = Double()
    var fireLonMax = Double()
    var fireLatMax = Double()
    var fireLonMin = Double()
    var fireLatMin = Double()
    
    
    override func viewDidLoad() {
    
    
        //Here goes some code to display on the SecondViewController
        readArray()
    
    }
    
    func readArray() {
    
        let placeRef = Firestore.firestore().collection("places")
            placeRef.getDocuments { (snapshot, error) in
            guard let snapshot = snapshot else {
                print("Error \(error!)")
                return
            }
            for document in snapshot.documents {
                let documentId = document.documentID
                let latMax = document.get("latMax") as! String //Getting a nil error
                print(documentId, latMax) //This print all objects
    
            }
        }
    }
    

    I'm missing something and I know it, the problem is that I can't find any reference of what I need to do in this case, I had read the documentation like 50 times and I can't find the solution. Thanks for taking the time to read my post.

    • Raj
      Raj almost 6 years
      Firwstore does not provide any method to fetch data of particular field of a document. You have to first fetch all the documents of a collection and then you can get the fields you want.
    • Robby
      Robby almost 6 years
      @Raj I try many different ways to accomplish accessing the fields after I fetch my documents but returns nil every time or my app crash! The way I try is accessing like an array[0] etc.
    • Raj
      Raj almost 6 years
      Can you plz add the complete code where you are calling the function and also the screenshot of the database
    • Jay
      Jay almost 6 years
      @Raj I know what your statement means, but to clarify a bit; You do not have to fetch all of the documents of a collection - you can fetch a single document and then access it's fields. Or query for a set of documents to which you can access individual fields within each document, or retrieve all the documents and access their fields. The example here Get Document will retrieve the contents of a single 'SF' document to which the individual fields can be accessed.
    • Robby
      Robby almost 6 years
      @raj I just add the screenshot of my Firesbase as well litlte changes on my code!
    • Robby
      Robby almost 6 years
      @Jay Is giving me a nil error! :(
    • Raj
      Raj almost 6 years
      Can u share the Model class @Robby
    • Robby
      Robby almost 6 years
      @Jay I edit the rest of the code with everything that I have, I hope that will give you more information.
  • Robby
    Robby almost 6 years
    I try to access the field following your code and returns nil, I believe is accessing a wrong field or maybe I'm missing something. I edit the code on my post!
  • Jay
    Jay almost 6 years
    @Robby AH - ok. I see the issue. The way the database is structured is the problem. You have multiple places listed under each collection and in this case I think you want a collection representing one place. I've updated my answer with a structure that will better suit what you are trying to do. The code I posted works with that structure
  • Jay
    Jay almost 6 years
    @Robby If you really want to have all of the places listed under one collection, that can be done, but I think denormalizing the data one level would be appropriate since the top level collection is called 'places' anyway, it's children should be each place.
  • Robby
    Robby almost 6 years
    this helps me a lot, the only problem that I have right now is how I suppose to struct my Firestore. I have to find the best way to struct my database and be able to get the best result! Any recommendation to learn a little bit more of Firestore! Thank you