Stack Overflow
Stack Overflow

Reputation: 19

How to run a function after another function in Swift 3

I am new to swift 3, and I got stuck to this problem. I have two functions, the first function gets the value of a key in my FirebaseDatabase. The second function displays a the variable retrieved from the FirebaseDatase inside a TableView. The problem is that the second function launches before the first one. This makes the value return nil.

First function:

        self.shopItems = [String]()



        databaseRef.observe(FIRDataEventType.value, with: { (snapshot) in
                for child in snapshot.children {
                    let snap = child as! FIRDataSnapshot
                    let dictionary = snap.value as! [String: AnyObject]
                    self.shopItems.append(dictionary["Name"] as! String)

                    print(self.shopItems)
            }

        })

The second function:

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    var cell = tableView.dequeueReusableCell(withIdentifier: self.cellReuseIdentifier, for: indexPath) as UITableViewCell

    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
        cell.textLabel?.text = self.shopItems[indexPath.row]
    })
    return cell
}

I know i can use the Dispatch-Wait method, but I want the second function to wait until the first one is done. How would i do so?

Upvotes: 1

Views: 1039

Answers (3)

Duncan C
Duncan C

Reputation: 131436

You are badly confused. You don't call tableView(_:cellForRowAt:). The system calls it.

If you want to wait and reload your table once the data has finished loading, you should put a call to reloadData inside your databaseRef.observe method's closure:

    databaseRef.observe(FIRDataEventType.value, with: { (snapshot) in
            for child in snapshot.children {
                let snap = child as! FIRDataSnapshot
                let dictionary = snap.value as! [String: AnyObject]
                self.shopItems.append(dictionary["Name"] as! String)

                print(self.shopItems)

                //Add the line below. 
                //Note that if the closure is called from the background, you'll 
                //need to use GCD to call this method on the main thread.
        }
        self.tableView.reloadData()
    }

And, if the completion code in your databaseRef.observe method gets called on a background thread then you'll need to wrap that in a call to Dispatch.main.async() (or a similar method to invoke the code on the main thread.)

DispatchQueue.main.async() {
    self.tableView.reloadData()
}

Upvotes: 1

Abishek Gokal
Abishek Gokal

Reputation: 186

The second function runs when data in the tableview is loaded. When the tableview is created it loads the data from shopitems array into the view. If there are no objects in the array the tableview will appear empty. In order to reflect changes to the shopitems array, in other words reflect that in item has been appended to the array you have to call the appropriate tableview function .

The correct way to do this is to use:

    databaseRef.observe(FIRDataEventType.value, with: { (snapshot) in
            for child in snapshot.children {
                let snap = child as! FIRDataSnapshot
                let dictionary = snap.value as! [String: AnyObject]
                self.shopItems.append(dictionary["Name"] as! String)

                let row = self.shopItems.index(of:self.shopItems.last!)
                let indexPath = IndexPath(row:row, section:0)
                tableView.insertItemsAtIndexPaths([indexPath])

                print(self.shopItems)
        }

    })

.You will have to create an indexPath using the index of the object in the shopitems array.

The other method is to reload all the data in the tableview after append.

    databaseRef.observe(FIRDataEventType.value, with: { (snapshot) in
            for child in snapshot.children {
                let snap = child as! FIRDataSnapshot
                let dictionary = snap.value as! [String: AnyObject]
                self.shopItems.append(dictionary["Name"] as! String)



                print(self.shopItems)
        }

              DispatchQueue.main.async {
            self.tableView.reloadData()
           }

    })

Upvotes: 0

Reinier Melian
Reinier Melian

Reputation: 20804

Try with this, as I said in my first comment I think you only need call self.tableView.reloadData after print(self.shopItems)

self.shopItems = [String]()
databaseRef.observe(FIRDataEventType.value, with: { (snapshot) in
    for child in snapshot.children {
        let snap = child as! FIRDataSnapshot
        let dictionary = snap.value as! [String: AnyObject]
        self.shopItems.append(dictionary["Name"] as! String)

        print(self.shopItems)
    }
    self.tableView.reloadData()
})

And correctly implement this method

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.shopItems.count
}

Also update your cellForRow to this

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    var cell = tableView.dequeueReusableCell(withIdentifier: 
    self.cellReuseIdentifier, for: indexPath) as UITableViewCell

    cell.textLabel?.text = self.shopItems[indexPath.row]
    return cell
}

Hope this helps

Upvotes: 2

Related Questions