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.
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