0

I´ve a webrequest with jsonserialization, after that, a for-in fetch process. In whole this takes approximately 5-7 seconds. After that i want to refersh my tableview in Viewcontroller. The scheme of the function looks like this.

public struct Container {
    let name: String
    let symbol: String
    let rank: String
}

public var dataArray = [Container]()


func fetchNewData() {

    var view = ViewController()

    // WebbRquest...

    // Json serialization...

    // the following list is much longer, will take a while...
    for items in json {
        let name = items["name"] as? AnyObject;
        let symbol = items["symbol"] as? AnyObject;
        let rank = items["rank"] as? AnyObject;
        let result = Container(name: name! as! String, symbol: symbol! as! String,rank: rank! as! String)

        dataArray.append(result)
    }

    // Now, after alle the work is done, i want to reload the tableview in Viewcontrller:
    view.reload()

    // Here i´m getting error, because nothing will be executed after return.

}

How can I call the reload function, after the webrequest process is finished? Because after the return, the function doesn´t execute anything anymore. And no other function will "know" when the fetchNewData() function is finished. Thanks for any help!

@IBAction func updateButton(_ sender: Any) {

     fetchNewData()

    }

According Phillipps suggestion, I had to modify the @IBAction func a little bit. But now it´s working. Awesome! Here the full working version:

public struct Container {
    let name: String
    let symbol: String
    let rank: String
}

public var dataArray = [Container]()


func fetchNewData(completion:@escaping ([Container])->()) {

    var view = ViewController()

    // WebbRquest...

    // Json serialization...

    // the following list is much longer, will take a while...
    for items in json {
        let name = items["name"] as? AnyObject;
        let symbol = items["symbol"] as? AnyObject;
        let rank = items["rank"] as? AnyObject;
        let result = Container(name: name! as! String, symbol: symbol! as! String,rank: rank! as! String)

        dataArray.append(result)
    }

    completion(dataArray)


}

This is the actionFunc:

 @IBAction func upDateButton(_ sender: Any) {

            let data = dataArray

            fetchNewData() {_ in (data)

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

 }
Josch Hazard
  • 323
  • 3
  • 20
  • Along with your other problems, this: `var view = ViewController()` is not going to help you. That creates a **new** ViewController that's not on the screen and knows nothing about the table you want to update. What object calls `fetchNewData`? (i.e. Where is the container array being used?) – Phillip Mills Jun 15 '17 at 17:42
  • Thanks, I just updated the code. I have to correct myself, not container is appending the result, its dataArray. This dataArray is shown in a tableview. – Josch Hazard Jun 15 '17 at 17:53
  • Is there a solution at all to access the reload function, which is (and has to be) in the ViewController class? – Josch Hazard Jun 15 '17 at 17:55
  • Still not clear where a call to `fetchNewData()` comes from.... Without details, all I can suggest is a pattern where your function accepts a closure as a parameter and executes it when `dataArray` is ready. The closure should access your **original** view controller and cause it to reload. – Phillip Mills Jun 15 '17 at 18:42
  • fetchNewData() comes from an an action func button from the viewcontroller. I´ve just realised that the throw atribute in my fetchNewData() function is not nessesary. I updated it in the code. (nevertheless it would be interesting how to perform an action after return). – Josch Hazard Jun 15 '17 at 19:21
  • Could you explain how how to set up the closure and how to access the view controller? – Josch Hazard Jun 15 '17 at 19:23

1 Answers1

1

Here's a start. It will be vague because I'm making guesses about code I can't see, but you may be able to convert it to your own needs.

Change the fetch function so that it takes a closure as a parameter:

func fetchNewData(completion:([Container])->()) {

...note that the closure will accept the data array when it's called. After you have your json all parsed, you then invoke the closure:

    dataArray.append(result)
}
completion(dataArray)

The "magic" is in the view controller where you tell fetchNewData what to do when it's finished. Something like:

@IBAction func updateButton(_ sender: Any) {
     fetchNewData() {(data)
        // Save the data where the view controller can use it
        self.tableArray = data

        // Main queue for UI update
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
     }
}

Note that the closure is written in the view controller, so self is the view controller. This means no need to create a second (useless) controller inside the fetch.

Phillip Mills
  • 30,888
  • 4
  • 42
  • 57