33

The error is: "fatal error: unexpectedly found nil while unwrapping an Optional value"

I am doing the following in ViewController:

var imageURL:UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()
    let url = NSURL(string:"http://cdn.businessoffashion.com/site/uploads/2014/09/Karl-Lagerfeld-Self-Portrait-Courtesy.jpg")
    let data = NSData(contentsOfURL:url!)
    if data!= nil {
        imageURL.image = UIImage(data:data!)
    }
}

I really don't understand why it will report an error on

imageURL.image = UIImage(data:data!)

while I already told it not to proceed if data is nil. It is not the problem of the link. Nor is there problem with the "data". I tried to print it and it was not nil.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Macondo
  • 369
  • 1
  • 3
  • 6
  • which answer is correct? I have same problem, but now am not sure what answer to follow because you did not specify one! – Engineeroholic Sep 04 '16 at 13:32
  • it will change depending on which version of swift you are using... I believe Airspeed Velocity's answer is the most viable for most cases – Jesus Rodriguez Dec 15 '16 at 21:41

16 Answers16

57

The error is most likely that imageURL is nil. Are you assigning it a value elsewhere in the code, or is it actually @IBOutlet in the real code? If you do not assign a value to it, it will be nil - but its type of UIImageView! means it is an "implicitly unwrapped optional" which means the compiler won't stop you using it even if it is nil, but will crash at runtime with the error you're getting.

The rest of the code is correct (assuming the missing space before != is a typo not in your compiling code), but you would be better off using if let to unwrap your optionals rather than checking them against nil and then using the force-unwrap operator:

if let url = NSURL(string: "http://etc...") {
    if let data = NSData(contentsOfURL: url) {
        imageURL.image = UIImage(data: data)
    }        
}

If you happen to be using the Swift 1.2 beta, you can combine the two ifs together:

if let url  = NSURL(string: "http://etc..."),
       data = NSData(contentsOfURL: url)
{
        imageURL.image = UIImage(data: data)
}

Or, if you prefer, use flatMap:

imageURL.image =
    NSURL(string: "http://etc...")
    .flatMap { NSData(contentsOfURL: $0) }
    .flatMap { UIImage(data: $0) }
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • 1
    Thank you so much!! I took your advice and created an outlet with an established ImageView from storyboard. Then I used method 1 you suggested. That is great!!! Thank you. – Macondo Apr 06 '15 at 14:59
  • You're welcome. If an answer answered your question, you can accept it by clicking the check mark next to it. – Airspeed Velocity Apr 06 '15 at 19:38
  • If I use `flatMap` and I put in a dummy URL, would the `UIImage` object be `nil` and it wouldn't result to a runtime error? – ton Dec 17 '15 at 11:28
  • can you help me do this asynchronously? this works but i want async – nikagar4 Jun 20 '16 at 13:06
17

Here is my code might help you

Swift 2:

extension UIImageView{

    func setImageFromURl(stringImageUrl url: String){

        if let url = NSURL(string: url) {
            if let data = NSData(contentsOfURL: url) {
                self.image = UIImage(data: data)
            }        
        }
    }
}

Swift 3:

extension UIImageView{

func setImageFromURl(stringImageUrl url: String){

      if let url = NSURL(string: url) {
         if let data = NSData(contentsOf: url as URL) {
            self.image = UIImage(data: data as Data)
         }
      }
   }
}

Usage

 let imgURL = "https://yourdomain.com/picturepath/picname.png" // or jpg
 self.myImage.setImageFromURl(stringImageUrl: imgURL)

if you are using HTTP connection and not https don't forget to add this

App Transport Security Settings as dictionary and into it Allow Arbitrary Loads as Boolean with value YES like in the below figure enter image description here

Anirudh R.Huilgol.
  • 839
  • 1
  • 15
  • 16
6

Here I have an approach from which you will not get any kind of crash when downloading image. Now currently you are using the following code:

override func viewDidLoad() {
    super.viewDidLoad()
    let url = NSURL(string:"http://cdn.businessoffashion.com/site/uploads/2014/09/Karl-Lagerfeld-Self-Portrait-Courtesy.jpg")
    let data = NSData(contentsOfURL:url!)
    if data!= nil {
        imageURL.image = UIImage(data:data!)
    }
}

Now change the code with this one, If you are continue with this approach:

override func viewDidLoad() {
    super.viewDidLoad()
    let url = NSURL(string:"http://cdn.businessoffashion.com/site/uploads/2014/09/Karl-Lagerfeld-Self-Portrait-Courtesy.jpg")
    let data = NSData(contentsOfURL:url!) 

    // It is the best way to manage nil issue. 
    if data.length > 0 {
        imageURL.image = UIImage(data:data!)
    } else {
        // In this when data is nil or empty then we can assign a placeholder image 
        imageURL.image = UIImage(named: "placeholder.png")
    }
}

And I am sure that If you are used it your nil crash will be solved.

Gourav Joshi
  • 2,419
  • 2
  • 27
  • 45
4

You can use SDWebImage for display image from URL https://github.com/rs/SDWebImage

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                      placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
Benny Bottema
  • 11,111
  • 10
  • 71
  • 96
Anita Nagori
  • 707
  • 1
  • 7
  • 21
3

Try writing:

if data != nil {}

instead:

if data!= nil {}

Compiler is maybe confusing exclamation mark with operation to unwrap the optional value.

Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159
3

You can set image in image view by using UIImageView+AFNetworking

Frist you drag the UIImageView+AFNetworking objective c classes in your project and import UIImageView+AFNetworking.h class in your project's bridge header file. And use this line to set image with placeholder in imageview. by this you can set multiple image in a view without any stuck.

yourImageview.setImageWithURL(NSURL(string: self.yourArray.objectAtIndex(indexPath.row).objectForKey("imageurl") as! String), placeholderImage: UIImage(named:"NoUserimage.png"))
pawan gupta
  • 251
  • 4
  • 7
3

Swift 3: (image in this case is 64 bit binary data)

if let url = NSURL(string: route) {
            if let imageData = NSData(contentsOf: url as URL) {
                let str64 = imageData.base64EncodedData(options: .lineLength64Characters)
                let data: NSData = NSData(base64Encoded: str64 , options: .ignoreUnknownCharacters)!
                let dataImage = UIImage(data: data as Data)

            }        
        }
Async-
  • 3,140
  • 4
  • 27
  • 49
  • This is a great piece of demonstration code to use some tricks to coerce data into the final form you need. – Zmart Dec 16 '17 at 08:05
2

Your code is right, just use:

if data != nil {

}
Undo
  • 25,519
  • 37
  • 106
  • 129
Rizwan Shaikh
  • 2,824
  • 2
  • 27
  • 49
2

It is pretty big possibility that you are using Alamofire and if so it is as simple as:

imageView.af_setImage(withURL: url)
Andrew
  • 36,676
  • 11
  • 141
  • 113
1

The problem is that at viewDidLoad the imageURL UIImageView may have not been set yet. I'd use optional chaining in case the UIImageView is nil

imageURL?.image = UIImage(data:data!)

I'd set the UIImageView's image at viewDidLayoutSubviews(), at which point you're sure that the ViewController's outlets have been set.

The way I'd do it would look like this:

@IBOutlet weak var imageURL: UIImageView!
var data: NSData?

override func viewDidLoad() {
    super.viewDidLoad()
    let url = NSURL(string:"http://cdn.businessoffashion.com/site/uploads/2014/09/Karl-Lagerfeld-Self-Portrait-Courtesy.jpg")
    data = NSData(contentsOfURL:url!)
    if data != nil {
        imageURL?.image = UIImage(data:data!)
    }
}

override func viewDidLayoutSubviews() {
    if data != nil {
        imageURL.image = UIImage(data:data!)
    }
}
1
NSURL(string: "") 

This returns an optional value. Take a look at description of it. It says An NSURL object initialized with URLString. If the URL string was malformed, returns nil.

In your code you are trying to url! which will get crash whenever the value of url is nil.

1

This is the code for it. I have used this you do not need to include classes or anything else. Just use this extension. This is very fast.

extension UIImageView {
    public func imageFromURL(urlString: String) {

        let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
        activityIndicator.frame = CGRect.init(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
        activityIndicator.startAnimating()
        if self.image == nil{
            self.addSubview(activityIndicator)
        }

        URLSession.shared.dataTask(with: NSURL(string: urlString)! as URL, completionHandler: { (data, response, error) -> Void in

            if error != nil {
                print(error ?? "No Error")
                return
            }
            DispatchQueue.main.async(execute: { () -> Void in
                let image = UIImage(data: data!)
                activityIndicator.removeFromSuperview()
                self.image = image
            })

        }).resume()
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Sandip Gill
  • 1,060
  • 1
  • 11
  • 22
0

Methode

extension UIImage {

/// Loads image asynchronously
///
/// - Parameters:
///   - url: URL of the image to load
///   - callback: What to do with the image

class func loadFromURL(url: NSURL, callback: (UIImage)->()) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {

        let imageData = NSData(contentsOfURL: url)
        if let data = imageData {
            dispatch_async(dispatch_get_main_queue(), {
                if let image = UIImage(data: data) {
                    callback(image)
                }
            })
        }
    })
}

}

Usage

    let logoUrl = ImageService().buildImageString((self.uiConfig?.headerImage)!, imageMode: .Uniform, size: self.headerImage.frame.size)
    UIImage.loadFromURL(logoUrl, callback: { (image: UIImage) -> () in
        self.headerImage.image = image
    }) 
0

Swift 3.0 for downloading image and parsing json using the URLSession and URLRequest.

static func getRequest(_ urlString:String, completion:@escaping (Any?) -> ()) {
    guard let url = URL(string: urlString) else { return }
    let session = URLSession.shared
    let request = URLRequest(url: url)

    let task = session.dataTask(with: request, completionHandler: {
        (data, response, error) -> Void in

        if let data = data {
            do {
                let json = try JSONSerialization.jsonObject(with: data, options: [])
                completion(json)
            } catch {
                print(error)
            }
        }
    })
    task.resume()
}

static func downloadImage(_ url: String, completion:@escaping(UIImage?)->()) {
    let aUrl = URL(string: url)
    DispatchQueue.global().async {
        do {
            let data = try? Data(contentsOf: aUrl!)
            completion(UIImage(data: data!))
        } catch {
            print("Unable to download image")
        }
    }
}
yo2bh
  • 1,356
  • 1
  • 14
  • 26
0
 let urlImg = NSURL(string:"https://firebasestorage.googleapis.com/v0/b/fanism-dccfe.appspot.com/o/Heros%2FIMG-20171116-WA0003.png?alt=media&token=b31a6d9e-cea6-422a-b198-82365abd845e")

    data = NSData.init(contentsOf: urlImg! as URL)
    if data != nil {
        imageView?.image = UIImage(data:data! as Data) //**Here imageView our ImageView outlet  **//
    }                                                                        
0

Swift 4: A simple loader for small images (ex: thumbnails) that uses NSCache and always runs on the main thread:

class ImageLoader {

  private static let cache = NSCache<NSString, NSData>()

  class func image(for url: URL, completionHandler: @escaping(_ image: UIImage?) -> ()) {

    DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {

      if let data = self.cache.object(forKey: url.absoluteString as NSString) {
        DispatchQueue.main.async { completionHandler(UIImage(data: data as Data)) }
        return
      }

      guard let data = NSData(contentsOf: url) else {
        DispatchQueue.main.async { completionHandler(nil) }
        return
      }

      self.cache.setObject(data, forKey: url.absoluteString as NSString)
      DispatchQueue.main.async { completionHandler(UIImage(data: data as Data)) }
    }
  }
}
Perette
  • 821
  • 8
  • 17
  • Hello. Could you please say is this answer may be related to my issue. I have to display image from JSON. It's structure is "logo": "https://s2.coinmarketcap.com/static/img/coins/64x64/1.png" – Abrcd18 Oct 29 '20 at 09:09