0

I'm fetching JSON data online and converting them to NSArray, and to String and class Arrays to manage the data. However, for some reason, the code exits the GetData() method after the line let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in

I've also verified that there was no issues with fetching the data, but it just can't be displayed on the table.

I've attached my project file also for download.

Codes from ViewController.swift

import UIKit

class ViewController: UIViewController, UITableViewDataSource {
var nameList = [NameManager]()

    @IBOutlet weak var NameTable: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        GetData()
        // Do any additional setup after loading the view, typically from a nib.
        NameTable.dataSource = self
        NameTable.reloadData()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func GetData(){
        let session = NSURLSession.sharedSession()
        let request = NSMutableURLRequest(URL: NSURL(string: "http://www.json-generator.com/api/json/get/bPfifKWNaq?indent=2")!)
        request.HTTPMethod = "GET"

        let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in

            if let error = error {
                print(error)
            }
            if let data = data{
                do{
                    let resultJSON = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
                    let resultArray = resultJSON as? NSArray
                    for jsonObjectString in resultArray!{
                        let code = jsonObjectString["code"] as! String
                        let name = jsonObjectString["name"] as! String
                        let description = jsonObjectString["description"] as! String
                        self.nameList.append(NameManager(code: code, name: name, description: description))
                    }
                    self.nameList.count

                }catch _{
                    print("Received not-well-formatted JSON")
                }

            }
            if let response = response {
                let httpResponse = response as! NSHTTPURLResponse
                print("response code = \(httpResponse.statusCode)")
            }

        })
        task.resume()

    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        let count = nameList.count
        return count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
        let myCell = NameTable.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as UITableViewCell

        myCell.textLabel?.text = nameList[indexPath.row].name
        myCell.detailTextLabel?.text = nameList[indexPath.row].description

        return myCell
    }

}

Codes from NameManager.swift

import Foundation
class NameManager{

    var code:String
    var name:String
    var description:String

    init(code: String, name: String, description: String){
        self.code = code
        self.name = name
        self.description = description
    }
}
5
  • Read some stuff about asynchrony. dataTaskWithRequest is asynchron. This means the block is called on a background thread. Commented Nov 12, 2015 at 7:43
  • I do not want it to fetch asynchronous, but synchronously. Because I need to do a count of items I have in my array, which now currently returns as 0. I need it to ensure that all data has been fetched before moving to the next item. How do I do so? Commented Nov 12, 2015 at 8:17
  • Simply don't. Find a way to use asynchronous calls. Synchronous calls freeze the UI. This is really bad UX in 2015. Commented Nov 12, 2015 at 8:22
  • By the way. Stick to the naming conventions of Swift. Instances start with a small letter. Commented Nov 12, 2015 at 8:23
  • @dasdom So from your advice, I changed return count to return 2 instead. But my program failed to execute myCell.textLabel?.text = nameList[indexPath.row].name Do you mind downloading from the link and try change? Commented Nov 12, 2015 at 8:34

1 Answer 1

3

session.dataTaskWithRequest is asynchronous and is automatically executed in background thread.

The dataTaskWithRequest is started when it sees the task.resume() and starts executing in background.

So, your program does not wait for its completion and starts to execute the instructions following it. In your example, your code will start to execute

    NameTable.dataSource = self
    NameTable.reloadData()

which are following GetData() method. Once the background execution is completed, the code you have in the completion handler is executed. So your tableView is not refreshed.

There are different ways you can approach this issue. one way is to include NameTable.reloadData() in your completion handler. Another way is to segue from a ViewController when background execution is completed.

Hope it helps.

EDIT:

import UIKit

class ViewController: UIViewController, UITableViewDataSource {
var nameList = [NameManager]()

    @IBOutlet weak var NameTable: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        NameTable.dataSource = self
        GetData()
        // Do any additional setup after loading the view, typically from a nib.

        //NameTable.reloadData()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func GetData(){
        let session = NSURLSession.sharedSession()
        let request = NSMutableURLRequest(URL: NSURL(string: "http://www.json-generator.com/api/json/get/bPfifKWNaq?indent=2")!)
        request.HTTPMethod = "GET"

        let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in

            if let error = error {
                print(error)
            }
            if let data = data{
                do{
                    let resultJSON = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
                    let resultArray = resultJSON as? NSArray
                    for jsonObjectString in resultArray!{
                        let code = jsonObjectString["code"] as! String
                        let name = jsonObjectString["name"] as! String
                        let description = jsonObjectString["description"] as! String
                        self.nameList.append(NameManager(code: code, name: name, description: description))
                    }
                    self.nameList.count
                    dispatch_async(dispatch_get_main_queue(), {
                        self.NameTable.reloadData()
                    })
                }catch _{
                    print("Received not-well-formatted JSON")
                }

            }
            if let response = response {
                let httpResponse = response as! NSHTTPURLResponse
                print("response code = \(httpResponse.statusCode)")
            }

        })
        task.resume()

    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        let count = nameList.count
        return count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
        let myCell = NameTable.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as UITableViewCell

        myCell.textLabel?.text = nameList[indexPath.row].name
        myCell.detailTextLabel?.text = nameList[indexPath.row].description

        return myCell
    }

}
Sign up to request clarification or add additional context in comments.

9 Comments

I do not want it to fetch asynchronous, but synchronously. Because I need to do a count of items I have in my array, which now currently returns as 0. I need it to ensure that all data has been fetched before moving to the next item. How do I do so?
There is no synchronous function with NSURLSession as you have with NSURLConnection. You have to manage it with dataTaskWithRequest.
So from your advice, I changed return count to return 2 instead. But my program failed to execute myCell.textLabel?.text = nameList[indexPath.row].name Do you mind downloading from the link and try change?
Sorry, I changed to dropbox. Please try again.
Just a quick question, what if I need to use the nameList object in another class? How do I pass the grabbed data over? I tried using static var sharedData = ViewController() to access in my other class, however it shows null.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.