parsing data into tableview swift 3

1.5k views Asked by At

I am attempting to parse data from a website and then display it into a tableview on the press of a button. I am using swift 3, Xcode 8.2 beta and can not get the data to store into an array or display into the tableView. Here is my tableViewCell class:

class TableViewCell: UITableViewCell {
@IBOutlet weak var userIdLabel: UILabel!
@IBOutlet weak var titleLabel: UILabel!
override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code
}

Here is my viewController code:

import UIKit
class SecondViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
let urlString = "https://jsonplaceholder.typicode.com/albums"
@IBOutlet weak var tableView: UITableView!
  var titleArray = [String]()
  var userIdArray = [String]()
@IBAction func getDataButton(_ sender: Any) {
    self.downloadJSONTask()
     self.tableView.reloadData()
}
override func viewDidLoad() {
    super.viewDidLoad()
     tableView.dataSource = self
     tableView.delegate = self
}
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

func downloadJSONTask() {
    let url = NSURL(string: urlString)
    var downloadTask = URLRequest(url: (url as? URL)!, cachePolicy:  URLRequest.CachePolicy.reloadIgnoringCacheData, timeoutInterval: 20)
    downloadTask.httpMethod = "GET"


    URLSession.shared.dataTask(with: (url! as URL),  completionHandler: {(Data, URLResponse, Error) -> Void in
        let jsonData = try? JSONSerialization.jsonObject(with: Data!,  options: .allowFragments)
           print(jsonData as Any)
        if let albumArray = (jsonData! as AnyObject).value(forKey: "") as? NSArray {
            for title in albumArray{
                if let titleDict = title as? NSDictionary {
                    if let title = titleDict.value(forKey: "title") {
                        self.titleArray.append(title as! String)
                        print("title")
                        print(title)
                    }
                    if let title = titleDict.value(forKey: "userId")    {
                        self.userIdArray.append(title as! String)
                    }
                    OperationQueue.main.addOperation ({
                        self.tableView.reloadData()
                    })
                }
            }                
        }        
    }).resume()       
    }
 func tableView(_ tableView: UITableView, numberOfRowsInSection  section: Int) -> Int{
    return titleArray.count
  }
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
    cell.titleLabel.text = titleArray[indexPath.row]
    cell.userIdLabel.text = userIdArray[indexPath.row]
    return cell
    }
    }
1

There are 1 answers

3
vadian On BEST ANSWER

There are many, many issues in your code, the worst is to use NSArray/NSDictionary in Swift.

The JSON is an array of dictionaries, the value for key title is String the value for userID is Int, so you have to declare your arrays

var titleArray = [String]()
var userIdArray = [Int]()

Never cast JSON data to most unspecified Any that's another no-go. Cast it always to the actual type. Another big problem is the Data parameter in the closure which clashes with the native struct in Swift3. Use always lowercase parameter labels. The request is not used at all in your code. And in Swift 3 use always the native structs URL, Data, URLRequest etc. Finally .allowFragments is nonsense since the JSON starts clearly with a collection type.

let url = URL(string: urlString)!
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 20)
URLSession.shared.dataTask(with: request) { (data, response, error) in
    if error != nil {
        print(error!)
        return
    }

    do {
        if let jsonData = try JSONSerialization.jsonObject(with:data!, options: []) as? [[String:Any]] {
            print(jsonData)
            for item in jsonData {

                if let title = item["title"] as? String {
                    titleArray.append(title)
                }
                if let userID = item["userId"] as? Int {
                    userIdArray.append(userID)
                }
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            }
        }
    } catch let error as NSError {
        print(error)
    }
}.resume()

PS: Using two separate arrays as data source is horrible, too. Imagine that one of the optional bindings might fail and the number of items in the arrays will be different. That's a pretty invitation for a runtime crash.