Search code examples
swiftalamofire

Unexpectedly found nil while unwrapping optional value image


Destination Controller

class CharDetailsViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var genderLabel: UILabel!
    @IBOutlet weak var houseLabel: UILabel!
    @IBOutlet weak var ancestryLabel: UILabel!

    var image = UIImage()
    var name = String()
    var gender = String()
    var house = String()
    var ancestry = String()

    override func viewDidLoad() {
        super.viewDidLoad()
        imageView.image = image
        nameLabel.text! = name
        houseLabel.text! = house

        // Do any additional setup after loading the view.
    }

Source Controller

class CharacterTableViewController: UITableViewController {

    var charactersData = [Character]()
    override func viewDidLoad() {
        super.viewDidLoad()
        loadData()
    }

    func loadData()
    {
        DispatchQueue.main.async {
            Alamofire.request("http://hp-api.herokuapp.com/api/characters").responseJSON(completionHandler: {
                (response) in
                switch response.result
                {
                case.success(let value):
                    let json = JSON(value)
                    print(json)
                    json.array?.forEach({
                        (character) in
                        let character = Character(name: character["name"].stringValue, house:character["house"].stringValue,image:character["image"].stringValue, gender: character["gender"].stringValue, ancestry: character["ancestry"].stringValue)
                        self.charactersData.append(character)
                    })
                    self.tableView.reloadData()
                case.failure(let error):
                    print(error.localizedDescription)
                }
            })
        }
    }
override func tableView( tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return charactersData.count
    }


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

        cell.nameLabel.text = "Name: " +  charactersData[indexPath.row].name
        cell.houseLabel.text = "House: " + charactersData[indexPath.row].house

        if let imageURL = URL(string: self.charactersData[indexPath.row].image) {
            DispatchQueue.global().async {
                let data = try? Data(contentsOf: imageURL)
                if let data = data {
                    let image = UIImage(data: data)
                    DispatchQueue.main.async {
                        cell.charImageView.image = image
                    }
                }
            }
        }
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let hpc = storyboard?.instantiateViewController(withIdentifier: "CharDetails") as? CharDetailsViewController

        hpc?.image = UIImage(named: charactersData[indexPath.row].image)! //Error found here

        hpc?.name = charactersData[indexPath.row].name
        hpc?.house = charactersData[indexPath.row].house
        self.navigationController?.pushViewController(hpc!, animated: true)
    }

class Character
{
    var name : String
    var house : String
    var image : String


    init(name: String, house: String, image: String)
    {
        self.name = name
        self.house = house
        self.image = image
    }
}

I'm getting a unexpectedly found nil while unwrapping an optional value image for passing the image to another controller. I have checked the outlets and they are perfectly fine. The images are grabbed from a url array as JSON Format as shown above, they are working fine on the table view and seen on the table view, but they are not showing up on the details view page. Kindly please assist me.


Solution

  • You are trying to forcefully unwrap UIImage, which is nil. UIImage(named:) method is used when the image is present inside the bundle and you wants to show it from there.

    hpc?.image = UIImage(named: charactersData[indexPath.row].image)! //Error found here
    

    On the above line, you are using UIImage(named:)! which means, you are force unwrapping it, that leads to crash with unexpectedly found nil while unwrapping an optional value

    Solution You can pass the image name same as the name and house,

    1) Update your

    class CharDetailsViewController: UIViewController {
      // Add this property to hold url string for image. 
      var imageUrlString = String()
    }
    

    2) in didSelectRow, update the code to pass the imageUrlString

    hpc?.imageUrlString = charactersData[indexPath.row].image
    

    2) put this in your viewDidLoad method of CharDetailsViewController

    if let imageURL = URL(string: imageUrlString) {
        DispatchQueue.global().async {
            let data = try? Data(contentsOf: imageURL)
            if let data = data {
                let image = UIImage(data: data)
                DispatchQueue.main.async {
                    self.imageView.image = image
                }
            }
        }
    }
    

    Hope it helps