1

I´m building a widget for iOS with Swift. The main app´s purpose is to connect to a URL news feed and get the latest news, while the widget only get the title to display in a tableView in the Today view.

I´ve written this method for the widget in order to get the data to populate the table, but for some reason nothing is showing. I´ve tried to debug it, but being a widget it seems to be practically imposible.

This is the cellForRowAt, where I connect to the feed and try to extract data. The funny part is, the main app uses basically the same code and it works perfectly.

     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)


      let urlRequest = URLRequest(url: URL(string: "https://newsapi.org/v1/articles?source=techcrunch&sortBy=top&apiKey=c64849bc30eb484fb820b80a136c9b0a")!)


      let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in

           do{

                let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]

                if let articlesFromJson = json["articles"] as? [[String: AnyObject]] {

                     if !(error != nil) {

                          var resultArray: NSMutableArray = NSMutableArray()

                          for articlesFromJson in articlesFromJson {

                               if let title = articlesFromJson["title"] as? String{
                                    resultArray.add(title)

                               }

                               let array:NSArray = resultArray.reverseObjectEnumerator().allObjects as NSArray
                               resultArray = array as! NSMutableArray
                               let title:String = resultArray.object(at: indexPath.row) as! String
                               cell.textLabel?.text = title

                          }
                     }            
                }

                //reload on main thread to speed it up
                DispatchQueue.main.async {
                 self.tableView.reloadData()
                }

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

      return cell
 }

If someone can help me figure out where is the mistake it would be a huge help, i´ve been stuck on this issue for days now. Thanks

2
  • 2
    Don't try to replicate your Objective-C habits in Swift. For example, don't use NSArray/NSMutableArray/NSDictionary/NSMutableDictionary, use Swift collections instead, etc. And avoid force-unwrapping and force-casting. Last suggestion: don't do network and parsing inside the tableView. Have the tableView use prepared content from another class whose job is downloading and parsing. Commented Dec 20, 2016 at 11:07
  • Thanks, I have put all the networking in another method like Justin suggested below too, I will look into using collections instead of NSMutableArrays. Commented Dec 20, 2016 at 12:48

2 Answers 2

3

You want to make your network request outside of cellForRow and then reloadData once it's complete to have the tableView reload the cells which calls cellForRow.

store the array of data outside of request so you can reference it from outside the function.

var resultArray: NSMutableArray = []

override func viewDidLoad() {
   super.viewDidLoad()
   getData()
 }     

func getData() {
  let urlRequest = URLRequest(url: URL(string: "https://newsapi.org/v1/articles?source=techcrunch&sortBy=top&apiKey=c64849bc30eb484fb820b80a136c9b0a")!)

  let task = URLSession.shared.dataTask(with: urlRequest) {[weak self] (data,response,error) in
    guard let strongSelf = self else { return }
    do{
        let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
        if let articlesFromJson = json["articles"] as? [[String: AnyObject]] {
            if error == nil {
                for articlesFromJson in articlesFromJson {
                    if let title = articlesFromJson["title"] as? String{
                        strongSelf.resultArray.add(title)
                    }
                    let array:NSArray = strongSelf.resultArray.reverseObjectEnumerator().allObjects as NSArray
                    strongSelf.resultArray = array as! NSMutableArray

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

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

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

  let title:String = resultArray.object(at: indexPath.row) as! String

  cell.textLabel?.text = title

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

6 Comments

Definitely follow Eric Aya advice above. This answer is focused on reorganization to obtain the desired result.
Thanks for the answer, regrettably I tried your suggestion but it doesn´t load the data neither, it actually says "Unable to load" now instead of the three empty tablerows before.
I did notice a mistake i made in original answer. I had it written as ' if error != nil ' { do what you would do if there wasn't an error }. I changed it to ' if error == nil '. That may have been why you were having the issue. make that change and let me know if you are still having a problem
I tried the change, but i still get the same Unable to load message
Unable to load seems to be an error associated with widgets. I am not familiar with how that works but I just googled it and saw other people have had issues with it in the past. I don't think the error is coming from this function. I recommend you google the error and see if you can find something that helps. If not, open up a separate question with that error.
|
0
checks proper if TableView delegate or datasource proper connected. and check array count before load data in cell 
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if (resultArray.count > 0){
      let title:String = resultArray.object(at: indexPath.row) as! String

  cell.textLabel?.text = title
}
else
{
  print("Error: resultArray contain nil value ")
}


  return cell
}

Comments

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.