Reputation: 53
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
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