johnyald
johnyald

Reputation: 53

Get value inside NSURLSession

My codes doesnt work, do you have an idea why? I want to display some data to my UITable from the requested HTTP

class contactView : UITableViewController{
    var values: NSMutableArray!

@IBOutlet var tbview: UITableView!

override func viewDidLoad() {

    if(signed != 1){
        self.navigationItem.title = ""
    }else{

        let outbtn = UIBarButtonItem(title: "Sign out", style: .Plain, target: self, action: #selector(contactView.out_action))
        navigationItem.leftBarButtonItem = outbtn

        let reloadData = UIBarButtonItem(title: "Reload", style: .Plain, target: self, action: #selector(contactView.loadData))
        navigationItem.rightBarButtonItem = reloadData

        //Check Connection

        if(reachability.isConnectedToNetwork() == true) {
            loadData()
        }else{
            let alert = UIAlertController(title: "Error Connection", message: "Not Internet Connection", preferredStyle: .ActionSheet)
            let alertAct = UIAlertAction(title: "I'll connect later !", style: .Destructive){
                (actions) -> Void in
            }
            alert.addAction(alertAct)
            self.presentViewController(alert, animated: true, completion: nil)
        }
    }

}
func loadData(){
    let url = NSURL(string: url_friends)

    let to_post = "user=iam&pin=101218"
    let request = NSMutableURLRequest(URL: url!)
    request.HTTPMethod = "POST"
    request.HTTPBody = to_post.dataUsingEncoding(NSUTF8StringEncoding)

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
        (let data,let response,let error) -> Void in

        dispatch_async(dispatch_get_main_queue(),{
            do{
                self.values = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSMutableArray
                print(self.values)

            }catch{
                print(error)
                return
            }
        })
    }
    task.resume()
}

I want to display the variable "value" data in my table but error keep occuring, saying it is nil when call in my table function cellForRowAtIndexPath

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

  let cell = tableView.dequeueReusableCellWithIdentifier("cellidentity") as UITableViewCell!
  let mainData = values[indexpath.row] as! String
  let x = cell.viewWithTag(2) as! UILabel

  if(signed != 1){
      print("No people")
  }else{
      let x = cell.viewWithTag(2) as! UILabel
      x.text = mainData["name"]

  }
  return cell
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let x : Int
    if(reachability.signed() != 1){
        x = 1
    }else{
        x = values.count        
    }
    return x
}

Upvotes: 2

Views: 110

Answers (1)

Rob
Rob

Reputation: 437381

Yes, the first time you load the table, it would be nil. The dataTaskWithRequest completion block has to explicitly call self.tableview.reloadData(), to tell the table to update itself now that the network request has finished. Remember, dataTaskWithRequest runs asynchronously, meaning that it finishes after the table is already presented. So you have to tell the table to reload itself (and therefore call the UITableViewDataSource methods again).

So you probably want something like:

let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {  data, response, error in
    guard data != nil && error == nil else {
        print(error)
        return
    }

    do {
        if let values = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSMutableArray {
            dispatch_async(dispatch_get_main_queue()) {
                self.value = values
                print(values)
                self.tableview.reloadData()
            }
        } 
    } catch let parseError {
        print(parseError)
        return
    }
}
task.resume()

Note, before doing forced unwrapping of data with data!, I first guard to make sure it's not nil. Never use ! unless you've know it cannot possibly be nil.


In terms of why your UITableView methods are failing the first time they're called, it's because they're relying upon reachability.signed() or signed. But the real question is whether values is nil or not.

So, perhaps:

var values: NSMutableArray?    // make this a standard optional, not an implicitly unwrapped one

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("cellidentity") as UITableViewCell!
    let x = cell.viewWithTag(2) as! UILabel    // btw, using `UITableViewCell` subclasses are more elegant than using cryptic `tag` numbers

    if let mainData = values?[indexPath.row] as? [String: String] {
        x.text = mainData["name"]
    } else {
        print("No people")
        x.text = "(retrieving data)"  // perhaps you want to tell the user that the request is in progress
    }
    return cell
}

// if `values` is `nil`, return `1`, otherwise returns `values.count`

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return values?.count ?? 1
}

Your code was configured to return one cell if the data was not available, so I've repeated that here, but generally I return zero rows if there's no data. If you do that, it simplifies cellForRowAtIndexPath even more, as it doesn't have to even worry about the "no data" condition at all. But that's up to you.

Now, I've made some assumptions above (e.g. that mainData was really a dictionary given that you are subscripting it with a string "name"). But less important than these little details is the big picture, namely that one should scrupulously avoid using ! forced unwrapping or using implicitly unwrapped optionals unless you know with absolute certainty that the underlying optional can never be nil.

Upvotes: 1

Related Questions