0

I am having trouble with trying to load image from my JSON api call. Basically I have created a function which will call to my API and fetch image URL which then will be shown in the second view controller. At the moment I can clearly see that my URL is passed correctly because I have print it in the console. However, when I try to update my UIImage I am getting an error saying:

fatal error: unexpectedly found nil while unwrapping an Optional value

This is my Model class file:

class Exercise {
private var _id: Int!
private var _exerciseURL: String!
private var _imageUrl: URL!

var id: Int {

    return _id
}

var exerciseURL: String {

    return _exerciseURL
}

var imageURL: URL {

    return _imageUrl // debug navigator is pointing at this line
}


init(name: String, description: String, id: Int) {
    self._id = id

    self._exerciseURL = "https://wger.de/api/v2/exerciseimage/?exercise=\(self.id)"

    self._imageUrl = URL(string: "")
}


func parseImageData() {

    let urlPath = _exerciseURL
    let url = URL(string: urlPath!)

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

        if error != nil {
            print("Error while parsing JSON")
        }
        else {

            do {
                if let data = data,
                    let fetchedImageData = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String:Any],
                    let images = fetchedImageData["results"] as? [[String: Any]] {


                    for eachImage in images {
                        let imageUrl = eachImage["image"] as! String
                        self._imageUrl = URL(string: imageUrl)
                    }

                    print(self.imageURL)


                }
            }
            catch {
                print("Error while parsing data.")
            }
        }
    }
    task.resume()
}

}

Here is my second view controller which should update image based on its url.

class ExerciseDetailViewController: UIViewController {

var exercise: Exercise!

@IBOutlet weak var exerciseImage: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()

    exercise.parseImageData()

    let data = NSData(contentsOf: exercise.imageURL)
    exerciseImage.image = UIImage(data: data as! Data)


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

}

whenever I try to comment out

let data = NSData(contentsOf: exercise.imageURL) 
exerciseImage.image = UIImage(data: data as! Data)

and then test my results I can see that my statement 'print(self.imageURL)' is correctly displaying URL in the second VC's console. Also, other variables of the same object such as name etc. which are not shown in the code are displayed correctly by other labels.

  • https://stackoverflow.com/questions/24643522/fatal-error-unexpectedly-found-nil-while-unwrapping-an-optional-values – UghThatGUyAgain Nov 28 '17 at 02:53
  • I am not sure if that's the case because whenever I try to comment out 'let data = NSData(contentsOf: exercise.imageURL) exerciseImage.image = UIImage(data: data as! Data)' and then test my results I can see that my statement 'print(self.imageURL)' is correctly displaying URL in the second VC's console. –  Nov 28 '17 at 03:12
  • Yeah I really don't know Swift, I'm just the guy that tries to help point people in the right direction with other posts without trying to shut down their question. – UghThatGUyAgain Nov 28 '17 at 03:16
  • Is `exercise.imageURL` also HTTPS-based, and not plain HTTP? – Michael Hulet Nov 28 '17 at 03:28
  • You have to make sure exercise.imageURL is in form of URL, not HTTPS/HTTP link in string. You have to convert it to URL form: let imageURL = URL(string: exercise.imageURL.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed)!) – YinKiet Nov 28 '17 at 07:40

1 Answers1

2

The problem is that your network request URLSession.shared.dataTask(with: url!) is asynchronous and you are accessing exercise.imageURL before the network request has completed and imageURL is still nil.

To get a better understanding of what's happening I encourage you to set breakpoints at the lines:

// 1. In parseImageData()
self._imageUrl = URL(string: imageUrl)

and

// 2. In viewDidLoad()
let data = NSData(contentsOf: exercise.imageURL)

You will see that the second breakpoint is activated first.

What you need to do is wait until the network request and parsing are complete. To do that, read about completion handlers in Swift and change parseImageData() to accept a completion handler. Check out this answer for an example function https://stackoverflow.com/a/43317287/2247399

Rob MacEachern
  • 811
  • 8
  • 13