Issues with retrieving information in Firestore

Hi I have been trying to download the data that my app is saving to firebase database but when i download the data the terminal is showing all of the data that i have uploaded but the app is only displaying the very last set of data. It seems like instead of saving it in an array is is just saving the last result. the code that i am using to get the data from firebase is
‘’’
func getFirebaseData() async → [JobDetial]{
let db = Firestore.firestore()
let collection = db.collection(“JobDetial”)

    do{
        let querySnapshot = try await collection.getDocuments()
          for document in querySnapshot.documents {
              let jobDetial = try document.data(as: JobDetial.self)
            print("\(document.documentID) => \(document.data())")
              return [jobDetial]
          }
        } catch {
          print("Error getting documents: \(error)")
    }
    return [JobDetial] ()
}

‘’’

@adamdt2007

Hi Adam,

Welcome to the community.

I think the answer is to try changing this line:

to:

let jobDetial = try document.data(as: [JobDetial.self])

What that does is define the data to be an array of JobDetail.

(Edit)
Hmmm, I might be right off the track here after having a look at some sample code in an App I am working on.

What is the definintion of JobDetail (ie, the struct or class that defines it)?

Hi Chris,
Thank you for the rapid response, JobDetail is defined as a struct at this time. When I try to make the change that you listed I get that this “Type of expression is ambiguous without type annotation” and it doesn’t seem to want the annotation of [JobDetail].
Thank you for your help

OK what you have to do is map the Firebase data items to the struct properties.

Let’s say for example our struct is:

struct JobDetail: Identifiable {
    var id: String
    var jobTitle: String
    var jobDescription: String
    var jobLocation: String
}

So the code from Firestone could be something like this to map the Firestore properties to the struct properties:

    func getFirebaseData() async -> [JobDetail] {
        let db = Firestore.firestore()
        let collection = db.collection("JobDetail")

        do {
            let querySnapshot = try await collection.getDocuments()
            let allJobs = querySnapshot.documents.map { document in
                return JobDetail(id: document.documentID,
                                 jobTitle: document["jobTitle"] as? String ?? "",
                                 jobDescription: document["jobDescription"] as? String ?? "",
                                 jobLocation: document["jobLocation"] as? String ?? ""
                )
                return allJobs
            }
        } catch {
            print("Error getting documents: \(error)")
        }
    }

I hope that helps in some way.

Thank you for your help, It works perfectly!

edit, it is still giving me some trouble with sub structs, I have tried a few different methods to get the data mapped. Thank you for any help you can give on this and all of the time that you have put into this

func getFirebaseData() async -> [JobDetail]{
        
        //set up Firestore basics
        let db = Firestore.firestore()
        let collection = db.collection("JobDetial")
        
        do{
            let querySnapshot = try await collection.getDocuments()
            let allJobs = querySnapshot.documents.map { document in
                return JobDetail(id: document.documentID,
                                 company: document["company"] as? String ?? "",
                                 jobName: document["jobName"] as? String ?? "",
                                 JobDetail.jobAddress.address: document["address"] as? String ?? "",
                                 JobDetail.jobAddress.city: document["city"] as? String ?? "",
                                 JobDetail.jobAddress.state: document["state"] as? String ?? "",
                                 JobDetail.jobAddress.zipCode: document["zipCode"] as? String ?? "",
                                 JobDetail.jobAddress.country: document["country"] as? String ?? "",
                                 JobDetail.Coordinate.latitude: document["latitude"] as? String ?? "",
                                 JobDetail.Coordinate.longitude: document["longitude"] as? String ?? "",
                                 jobImageURL: document["jobImageURL"] as? [ String] ?? [],
                                 jobCreationDate: document["jobCreationDate"] as? String ?? "",
                                 jobCreatedBy: document["jobCreatedBy"] as? String ?? "",
                                 active: document["active"] as? Bool ?? true,
                                 jobPM: document["jobPM"] as? String ?? ""
                )
              }
            return allJobs
            } catch {
              print("Error getting documents: \(error)")
        }
        return [JobDetail] ()
    }

for the struct I have

**struct** JobDetail: Identifiable, Decodable, Encodable {

**var** id: String?

**var** company: String?

**var** jobName: String?

**var** jobAddress: Location?

**var** jobLocation: Coordinate?

**var** jobImageURL: [String]?

**var** jobCreationDate: String?

**var** jobCreatedBy: String?

**var** active: Bool?

**var** jobPM: String?

**var** jobStaff: [PersonDetail]?

**var** reports: [ReportDetail]?

}

**struct** Location: Decodable, Encodable {

**var** address: String?

**var** city: String?

**var** country: String?

**var** state: String?

**var** zipCode: String?

}

**struct** Coordinate: Decodable, Encodable {

**var** latitude: String?

**var** longitude: String?

}

@adamdt2007
Hi Adam,

This did my head in for a while but I finally figured it out.

One thing you have to be really careful about is the naming convention you use with your structs (or classes for that matter) to avoid using a Swift reserved word. For example the struct naming of Location and Coordinate are both from built in API’s that use those two names. Whilst this might work in this scenario there is always a chance that the complier will get confused. So what I did as part of this solution is to rename those two to be more explicit.

struct JobDetail: Identifiable, Decodable, Encodable {
    var id: String
    var company: String
    var jobName: String
    var jobAddress: JobLocation
    var jobLocation: JobCoordinate
    var jobImageURL: [String]
    var jobCreationDate: String
    var jobCreatedBy: String
    var active: Bool
    var jobPM: String
//    var jobStaff: [PersonDetail]?
//
//    var reports: [ReportDetail]?
}

struct JobLocation: Decodable, Encodable {
    var address: String
    var city: String
    var country: String
    var state: String
    var zipCode: String
}

struct JobCoordinate: Decodable, Encodable {
    var latitude: String
    var longitude: String
}

Note that I commented out the jobStaff and reports attributes as that was causing errors for me since there was nothing in your Firebase code to provide data for ether.

and for the getFirebaseData function, the code changes to this:

    func getFirebaseData() async -> [JobDetail] {
        //set up Firestore basics
        let db = Firestore.firestore()
        let collection = db.collection("JobDetial")
        
        do{
            let querySnapshot = try await collection.getDocuments()
            let allJobs = querySnapshot.documents.map { document in
                return JobDetail(id: document.documentID,
                                 company: document["company"] as? String ?? "",
                                 jobName: document["jobName"] as? String ?? "",
                                 jobAddress: JobLocation(address: document["address"] as? String ?? "",
                                                      city: document["city"] as? String ?? "",
                                                      country: document["country"] as? String ?? "",
                                                      state: document["state"] as? String ?? "",
                                                      zipCode: document["zipCode"] as? String ?? ""),
                                 jobLocation: JobCoordinate(latitude: document["latitude"] as? String ?? "",
                                                    longitude: document["longitude"] as? String ?? ""),
                                 jobImageURL: document["jobImageURL"] as? [String] ?? [""],
                                 jobCreationDate: document["jobCreationDate"] as? String ?? "",
                                 jobCreatedBy: document["jobCreatedBy"] as? String ?? "",
                                 active: document["active"] as? Bool ?? true,
                                 jobPM: document["jobPM"] as? String ?? ""
                )
            }
            return allJobs
        } catch {
            print("Error getting documents: \(error)")
        }
        return [JobDetail] ()
    }

For future posts, when you want to add a code block from Xcode the three backtick characters preceding the code should look like this:
```
They are the backwards facing character on the same key as the tilde ~ (at least that is the case on my QWERTY keyboard here in Australia).

The other way to get a code block in place is to get a fresh line in the editing window and then tap on the </> button at the top and you will get a block with the 3 backticks above and below with the words “type or paste code here” in between so you simply paste your code in place of the those words.

Hope that helps you out.

Firebase is tricky but when you master (and I don’t claim to be at that level yet) it I think it’s a great solution for iOS Apps.

Cheers
Chris P

Hi Chris, Thank you so much for your help! That finally got it, I was so close as I had something similar but I don’t think I would have gotten to this.

And just a note for anyone who is looking at this after the fact to help them, I had to change my data in Firebase from being mapped to the field of jobAddress to just being another top level field for this to work. For my data it’s not a big deal and I’m sure there is a way to get this to work but I was able to make that change without any impacts.

Oh that’s interesting, did you change it subsequent to the solution that I sent or prior to that? From the code you sent me I assumed that the data was from a single collection rather than a collection within a collection.

Nevertheless I am pleased that it worked for you. I enjoyed the challenge. :grinning:

I did it subsequent to the solution, when I initially put in the solution it returned “” so i tested by changing the ?? “” to ?? “No Data” to see if it was a data issue or a view issue and i got back the no data. once i changed jobAddress and jobLocation from a mapped field in firestore to a top level field in the document. I did not have to make any changes with my structs in Xcode.

Thank you again for your help and on some level i’m glad that it was a bit of a challenge as it means that I want asking something that I could have easily figured out.