Reem Sameer Reem Sameer - 5 months ago 16
iOS Question

Parsing image from iTunes using JSON to tableviewcontroller

I'm trying to parse data from this data source, Titles are parsed correctly and displayed, but the problem occurred when parsing the images, I got an error: “fatal error: unexpectedly found nil while unwrapping an Optional value”
Here is my Code:

ViewModel.swift

import Foundation

class ViewModel {

let urlString = "http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/topsongs/limit=30/json"

var titles = [String]()

var images = [String]()



func fetchTitles(success:() -> ()) {

let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())

let url = NSURL(string: urlString)

let task = session.dataTaskWithURL(url!) { (data, response, error) -> Void in

let parser = JSONParser()

self.titles = parser.titlesFromJSON(data!)

success()

}

task.resume()

}

func fetchImages(success:() -> ()) {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())

let url = NSURL(string: urlString)

let task = session.dataTaskWithURL(url!) { (data, response, error) -> Void in

let parser = JSONParser()
self.images = parser.imagesFromJSON(data!)
success()
}
task.resume()
}

func numberOfSections() -> Int {
return 1
}

func numberOfItemsInSection(section: Int) -> Int {
return titles.count
}

func titleForItemAtIndexPath(indexPath: NSIndexPath) -> String {
return titles[indexPath.row]
}

}


and MyTableViewController.swift

import UIKit

class MyTableViewController: UITableViewController {

let viewModel = ViewModel()

var imageCache = [String:UIImage]()



override func viewDidLoad() {

super.viewDidLoad()

self.refresh()

self.refreshControl = UIRefreshControl()

self.refreshControl?.addTarget(self, action: #selector(MyTableViewController.refresh), forControlEvents: .ValueChanged)



}



func refresh() {

viewModel.fetchTitles {

dispatch_async(dispatch_get_main_queue()) {

self.tableView.reloadData()

self.refreshControl?.endRefreshing()

}

}

viewModel.fetchImages {

dispatch_async(dispatch_get_main_queue()) {

self.tableView.reloadData()

self.refreshControl?.endRefreshing()

}

}



}



override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

return self.viewModel.numberOfSections()

}



override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return self.viewModel.numberOfItemsInSection(section)

}



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

let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MyTableViewCell

cell.songTitle.text = self.viewModel.titleForItemAtIndexPath(indexPath)




let urlString = self.viewModel.titleForItemAtIndexPath(indexPath)

//found nil

let imgURL: NSURL = NSURL(string: urlString)!

let request: NSURLRequest = NSURLRequest(URL: imgURL)

NSURLConnection.sendAsynchronousRequest(

request, queue: NSOperationQueue.mainQueue(),

completionHandler: {(response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in

if error == nil {

cell.songImage.image = UIImage(data: data!)
}

})

return cell

}


and JSONParser.swift

import Foundation

class JSONParser {



func titlesFromJSON(data: NSData) -> [String] {

var titles = [String]()



do {

if let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject],

let feed = json["feed"] as? [String: AnyObject],

entries = feed["entry"] as? [[String: AnyObject]] {



for entry in entries {

if let name = entry["im:name"] as? [String: AnyObject],

label = name["label"] as? String {

titles.append(label)

}

}

}

} catch {

print("error parsing JSON: \(error)")

}



return titles

}



func imagesFromJSON(data: NSData) -> [String] {

var images = [String]()



do {

if let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject],

let feed = json["feed"] as? [String: AnyObject],

entries = feed["entry"] as? [[String: AnyObject]] {



for entry in entries {

if let name = entry["im:image"]![0] as? [String: AnyObject],

label = name["label"] as? String {

images.append(label)

}

}

}

} catch {

print("error parsing JSON: \(error)")

}

return images
}

}


And I have a class MyTableViewCell subclass of UITableViewCell containing a UILabel and a UIImageView.
I can't figure out what's the problem, and why I'm getting this error. Thank you for your time.

Answer

I always recommend you to avoid the use of force-unwrapping and adopt a more secure way of code (e.g using optional binding) avoiding runtime errors. So you can change your code to the following code and avoid the runtime error:

for entry in entries {
    if let name = entry["im:image"], let value = name[0] as? [String: AnyObject],
         label = value["label"] as? String {
             images.append(label)
    }
}

In the above way you avoid the use of force-unwrapping.

I hope this help you.

Comments