Shaft jackson
Shaft jackson

Reputation: 105

How to return data in array from Firestore?

I am building an IOS app and I am at the stage where I want to load some categories in a pickerview. The list of categories is stored in Firestore as documents.

I have a function that does the following:

  1. Initiate an array
  2. Read data from Firestore
  3. Append the array with values
  4. Return the array

The problem is that the function seems to return the array without even getting any results from Firestore

My data model

class categoryModel {
    var catImg=""
    var catName=""
    var catID=""
    //set categories
    init(catImg: String, catName:String, catID:String){
        self.catName=catName
        self.catImg=catImg
        self.catID=catID
    }
}

Firestore reference

struct FirestoreReferenceManager {

    static let event = "events"

    static let db = Firestore.firestore()
    static let rootEvents = db.collection("events")
    static let rootUsers = db.collection("users")

}

Firestore service

class FirestoreService {

    static func getCategoryfromDB()->[categoryModel] {

        let rootCategory = FirestoreReferenceManager.rootEvents
        var tabCategory=[categoryModel]()


        rootCategory.getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                for document in querySnapshot!.documents {

                    tabCategory.append(categoryModel(catImg: document.data()["img"] as! String, catName:document.data()["name"] as! String, catID: document.documentID))

                    print("number of categories running: \(tabCategory.count)")
                }
            }

        }
        print("number of categories final: \(tabCategory.count)")
        return tabCategory
    }
}

Check what the log is showing

umber of categories final: 0
number of categories final: 0
number of categories running: 1
number of categories running: 2
number of categories running: 3
number of categories running: 4
number of categories running: 5
number of categories running: 6
number of categories running: 7
number of categories running: 8
number of categories running: 9
number of categories running: 10
number of categories running: 11
number of categories running: 1
number of categories running: 2
number of categories running: 3
number of categories running: 4
number of categories running: 5
number of categories running: 6
number of categories running: 7
number of categories running: 8
number of categories running: 9
number of categories running: 10
number of categories running: 11

The 2 first lines should actually be the lasts.

And when I read Firestore from a viewcontroller, the array is empty. I tried hardcoding the data model and it is working fine.

I read about some "delay" in getting data, but then how to fix this?

How do I solve the problem?

Upvotes: 0

Views: 2187

Answers (3)

Shaft jackson
Shaft jackson

Reputation: 105

Thanks to @arvidure and others, i managed to have the code below that works

My data model

class categoryModel {
    var catImg=""
    var catName=""
    var catID=""
    //set categories
    init(catImg: String, catName:String, catID:String){
        self.catName=catName
        self.catImg=catImg
        self.catID=catID
    }
}

Firestore reference

struct FirestoreReferenceManager {

    static let event = "events"

    static let db = Firestore.firestore()
    static let rootEvents = db.collection("events")
    static let rootUsers = db.collection("users")

}

Firestore service

class FirestoreService {

    static func getCategoryfromDB(dispatch:DispatchGroup, completed: @escaping ([categoryModel]) -> Void) {

        let rootCategory = FirestoreReferenceManager.rootEvents
        var tabCategory=[categoryModel]()
        //let dispatch = DispatchGroup()

        dispatch.enter()

        rootCategory.getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                for document in querySnapshot!.documents {

                    tabCategory.append(categoryModel(catImg: document.data()["img"] as! String, catName:document.data()["name"] as! String, catID: document.documentID))

                    print("number of categories running: \(tabCategory.count)")
                }
            }  
            dispatch.leave()
        }

        dispatch.notify(queue: .main, execute: {
            print("number of categories : \(tabCategory.count)")
            completed(tabCategory)

        })

    }
}

And this is how I call it in the viewcontroller

super.viewDidLoad()

        let dispatch = DispatchGroup()
        category=CategoryModelPicker()
        //category.modelCategory=categoryData.getCategory()
        //category.modelCategory=FirestoreService.getCategoryfromDB(completed: category?.modelCategory)
        FirestoreService.getCategoryfromDB(dispatch:dispatch){(cat) in
            dispatch.notify(queue: .main, execute: {
                print("number of categories final: \(cat.count)")
                self.category.modelCategory=cat

                //Additional code

            })
        }
}

Thanks for the help :)

Upvotes: 2

arvidurs
arvidurs

Reputation: 3033

Something like this should do it. The dispatch is a group where you can add closures to. Once all of them are finished the notify closure is called, where you can return the data. Read up on dispatch groups, which will help.

static func getCategoryfromDB(completed: @escaping ([categoryModel]) -> Void) {

        let rootCategory = FirestoreReferenceManager.rootEvents
        var tabCategory=[categoryModel]()
        let dispatch = DispatchGroup()
        dispatch.enter()
        rootCategory.getDocuments() { (querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                for document in querySnapshot!.documents {

                    tabCategory.append(categoryModel(catImg: document.data()["img"] as! String, catName:document.data()["name"] as! String, catID: document.documentID))

                    print("number of categories running: \(tabCategory.count)")
                }
            }
            dispatch.leave()

        }
        dispatchGroup.notify(queue: .main, execute: {
            print("number of categories final: \(tabCategory.count)")
            completed(tabCategory)

        })


        }

Upvotes: 2

MQLN
MQLN

Reputation: 2328

It looks like you're running into issues from the asynchrony of your code.

Your function is calling return tabCategory before your code contained within the brackets that actually gets your documents actually runs.

Use an escaping closure to call a function when your async process finishes.

Your finished product will look something like this:

static func getCategoryfromDB(completed: @escaping (([categoryModel]?, Error?)) -> Void) {

    let rootCategory = FirestoreReferenceManager.rootEvents

    rootCategory.getDocuments() { (querySnapshot, err) in
        if let err = err {
            tabCategory(nil, err)
        } else {
            var tabCategory=[categoryModel]()
            for document in querySnapshot!.documents {

                tabCategory.append(categoryModel(catImg: document.data()["img"] as! String, catName:document.data()["name"] as! String, catID: document.documentID))

                print("number of categories running: \(tabCategory.count)")
            }
          gotData(tabCategory, nil)
        }
    }
}

Upvotes: 0

Related Questions