2

This is my code for making a network call to forecast.io. Inside the ViewController I have:

private let apiKey = ""//my key

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)")
    let forecastURL = NSURL(string: "37.8267,-122.423", relativeToURL : baseURL)

    let sharedSession = NSURLSession.sharedSession()
    let downloadTask : NSURLSessionDownloadTask = sharedSession.downloadTaskWithURL(forecastURL!, completionHandler: { (location: NSURL!, response: NSURLResponse!, error: NSError!) -> Void in
        if (error == nil) {
            let dataObject = NSData(contentsOfURL: location)
            let weatherDictionary : NSDictionary = NSJSONSerialization.JSONObjectWithData(
                dataObject!, options: nil, error: nil) as! NSDictionary
        }
    })
    downloadTask.resume()
}

I'm trying to set my data into an NSDictionary to be able to access it. I have a bug (green line) which has something to do with weatherDictionary:

fatal error: unexpectedly found nil while unwrapping an Optional value

I'm unwrapping the dataObject, so what could be the problem?

honk
  • 9,137
  • 11
  • 75
  • 83
ecoguy
  • 655
  • 5
  • 16
  • weatherDictionary should have a nil value. Please check dataObject – Ritu May 15 '15 at 09:50
  • Have you verified that valid JSON is returned? And that the JSON really represents a *dictionary*? – Btw ([I am repeating myself](http://stackoverflow.com/questions/30219596/unable-to-make-network-call#comment48544525_30219596)): Use the error parameter!! – Martin R May 15 '15 at 09:52
  • commenting out weatherdictionary and println(dataObject) tells me it's not JSON. But isn't weatherDictionary supposed to make it JSON? If not that could be the problem since dataObject is just a row of numbers. – ecoguy May 15 '15 at 10:00

1 Answers1

3

You really, seriously, need to get out of the habit of force-unwrapping. If whenever you get an optional, you just use ! to unwrap it, you are going to be forever hitting these problems.

Here’s a version of your inner code that checks the optionals at each turn:

let sharedSession = NSURLSession.sharedSession()
let downloadTask = sharedSession.downloadTaskWithURL(forecastURL!)
{ location, response, error in

    if let error = error {
        // log error
    }
    else if let dataObject = NSData(contentsOfURL: location) {

        let weatherObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(
            dataObject, options: nil, error: nil)

        if let weatherDictionary = weatherObj as? NSDictionary {

        }
        else {
            // log error with conversion of weather to dictionary
        }

    }
    else {
        // log error with dataObject
    }
}

Yes, this is longer and more annoying to write (though, type inference will help go the other way – you don’t have to explicitly type everything e.g. in the callback, it’s clearer IMO to leave the types off).

Yes, sometimes you know for sure that a value won’t be nil, so it’s easier and cleaner just to force-unwrap it (e.g. with your NSURL – you can be pretty safe with that one so long as no user-input is involved).

But until you get to the point where you don’t bash your head against nil-value errors constantly, it’s better to write your code this way.

Once you get more comfortable with handling optionals, explore the other techniques to write neater code.

You might also want to consider using stronger typing when processing the JSON results, for example, you could do the following:

if let weatherDictionary = weatherObj as? [String:AnyObject]

and so on when processing the inside of the dictionary. Again, if you trust forecast.io to always give you valid JSON data in exactly the right form, you can skip this step and force everything but that will be harder to debug when writing the code, and you risk your code blowing up in production (as opposed to failing gracefully) if you ever get corrupt data back.

Community
  • 1
  • 1
Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118