Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Build a Weather App with Swift (Retired) Displaying Our Weather Data The End!

Justin Bane
Justin Bane
7,850 Points

CoreLocation: fatal error: unexpectedly found nil while unwrapping an Optional value

Yeah right try to hookup location detection in the app, right. So this has been asked and not answered. I can get the location all day long for where ever I set the location in the location file.

When I try to pass the string (which prints just fine) into the forecastURL NSURL call - I get this "fatal error: unexpectedly found nil while unwrapping an Optional value"

Can ANYONE HELP!

here is my ViewController in whole

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {

    var manager: CLLocationManager!
    var currentLocation: CLLocation!

    private let apiKey = "#################"

    @IBOutlet weak var iconView: UIImageView!
    @IBOutlet weak var currentTimeLabel: UILabel!
    @IBOutlet weak var temperatureLabel: UILabel!
    @IBOutlet weak var humidityLabel: UILabel!
    @IBOutlet weak var precipitationLabel: UILabel!
    @IBOutlet weak var summaryLabel: UILabel!
    @IBOutlet weak var refreshButton: UIButton!
    @IBOutlet weak var refreshActivityIndicator: UIActivityIndicatorView!


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        refreshActivityIndicator.hidden = true

        manager = CLLocationManager()
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        manager.requestAlwaysAuthorization()


    }

    func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        self.manager.stopUpdatingLocation()
        if status == .Authorized || status == .AuthorizedWhenInUse {
            self.manager.startUpdatingLocation()
            self.currentLocation = self.manager.location
            self.getCurrentWeatherData()
        }

    }

    func getCurrentWeatherData() -> Void {

        var locationString: String = ""
        if let lat = self.currentLocation?.coordinate.latitude {
            locationString = "\(lat)"
        }
        if let lon = self.currentLocation?.coordinate.longitude {
            locationString += ", \(lon)"
        }


        let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/")
        let forecastURL = NSURL(string: "\(locationString)", 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

                let currentWeather = Current(weatherDictionary: weatherDictionary)
                // send to the dispatcher asycronously
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    self.temperatureLabel.text = "\(currentWeather.temperature)"
                    self.iconView.image = currentWeather.icon!
                    self.currentTimeLabel.text = "At \(currentWeather.currentTime!) it is"
                    self.humidityLabel.text = "\(currentWeather.humidity)"
                    self.precipitationLabel.text = "\(currentWeather.precipProbability)"
                    self.summaryLabel.text = "\(currentWeather.summary)"

                    // Stop button animation
                    self.refreshActivityIndicator.stopAnimating()
                    self.refreshActivityIndicator.hidden = true
                    self.refreshButton.hidden = false

                })

            } else {
                let networkIssueController = UIAlertController(title: "Error", message: "Unable to load data due to connectivity issue", preferredStyle: .Alert)

                let okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
                networkIssueController.addAction(okButton)

                let cancelButton = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
                networkIssueController.addAction(cancelButton)

                self.presentViewController(networkIssueController, animated: true, completion: nil)

                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    // Stop button animation
                    self.refreshActivityIndicator.stopAnimating()
                    self.refreshActivityIndicator.hidden = true
                    self.refreshButton.hidden = false
                })
            }

        })

        downloadTask.resume()

    }


    @IBAction func refresh() {

        getCurrentWeatherData()
        refreshButton.hidden = true
        refreshActivityIndicator.hidden = false
        refreshActivityIndicator.startAnimating()

    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

3 Answers

Justin Bane
Justin Bane
7,850 Points

ARRRRGGGGHHHH!

ladies and gentlman - it was the space.

here is the line of code that killed it.

locationString = "\(lat), \(lon)"

see the space after the comma, NSURL no likey

locationString = "\(lat),\(lon)"

removed space and all is well.... theres a day of my life I will never get back.

Pasan Premaratne
STAFF
Pasan Premaratne
Treehouse Teacher

Justin Bane,

Seems like you might be calling getCurrentWeather() before the CLLocation object is set. Most straight forward way would be to refactor the method to something like getCurrentWeather(lat: Double, long: Double) and call the method directly from within locationManager: didUpdateLocations:, passing the coordinates when you are certain they are not nil.

Quick notes about your code.

var locationString: String = ""
        if let lat = self.currentLocation?.coordinate.latitude {
            locationString = "\(lat)"
        }
        if let lon = self.currentLocation?.coordinate.longitude {
            locationString += ", \(lon)"
        }

In this section you're not accounting for else cases. For example if, for some reason, the first optional binding fails and lat is nil, but lon is not, then you will have a string that looks like ",\(lon)" which will create an invalid URL. You only want to create the string if both cases are not nil. Do this via a nested if let construct like so :

var locationString: String
if let lat = self.currentLocation?.coordinate.latitude {
    if let lon = self.currentLocation?.coordinate.longitude {
        locationString = "\(lat),\(lon)"
    }
}

or if you are using Swift 1.2, you can check for multiple values in a single statement:

var locationString: String
if let lat = self.currentLocation?.coordinate.latitude, lon = self.currentLocation?.coordinate.longitude {
    locationString = "\(lat),\(lon)"
}

Here is how I set it up:

//LOCATION LOCATION LOCATION 

    func initLocationManager() {
        seenError = false
        locationFixAchieved = false
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        locationManager.requestAlwaysAuthorization()
        locationManager.startUpdatingLocation()
        locationManager.requestWhenInUseAuthorization()
    }

    func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
        locationManager.stopUpdatingLocation()
        if ((error) != nil) {
            if (seenError == false) {
                seenError = true
                print(error)
            }
        }
    }


    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: {(placemarks, error)->Void in

            let pm = placemarks[0] as! CLPlacemark
            self.displayLocationInfo(pm)
            })

        if (locationFixAchieved == false) {
            locationFixAchieved = true
            var locationArray = locations as NSArray
            var locationObj = locationArray.lastObject as! CLLocation
            var coord = locationObj.coordinate
            self.userLatitude = coord.latitude
            self.userLongitude = coord.longitude

            getCurrentWeatherData()


        }
    }

    func displayLocationInfo(placemark: CLPlacemark?) {
        if let containsPlacemark = placemark {
            //stop updating location to save battery life
            locationManager.stopUpdatingLocation()
            let locality = (containsPlacemark.locality != nil) ? containsPlacemark.locality : ""
            let postalCode = (containsPlacemark.postalCode != nil) ? containsPlacemark.postalCode : ""
            let administrativeArea = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea : ""
            let country = (containsPlacemark.country != nil) ? containsPlacemark.country : ""
            //println(locality)
            //println(postalCode)
            //println(administrativeArea)
            //println(country)

            self.userLocationLabel.text = "\(locality), \(administrativeArea)"
        }
    }

    func locationManager(manager: CLLocationManager!,
        didChangeAuthorizationStatus status: CLAuthorizationStatus) {
            var shouldIAllow = false

            switch status {
            case CLAuthorizationStatus.Restricted:
                locationStatus = "Restricted Access to location"
            case CLAuthorizationStatus.Denied:
                locationStatus = "User denied access to location"
            case CLAuthorizationStatus.NotDetermined:
                locationStatus = "Status not determined"
            default:
                locationStatus = "Allowed to location Access"
                shouldIAllow = true
            }
            NSNotificationCenter.defaultCenter().postNotificationName("LabelHasbeenUpdated", object: nil)
            if (shouldIAllow == true) {
                NSLog("Location to Allowed")
                // Start location services
                locationManager.startUpdatingLocation()
            } else {
                NSLog("Denied access: \(locationStatus)")
            }
    }


    //WEATHER

    func getCurrentWeatherData() -> Void {

        userLocation = "\(userLatitude),\(userLongitude)"

        let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/")
        let forecastURL = NSURL(string: "\(userLocation)", relativeToURL:baseURL)
Justin Bane
Justin Bane
7,850 Points

Your code does not work either. same error - I have found some many crap explanations. It doesnt help that apple keeps changing swift everytime there is an update.

Any help here people, seriously about to just dump this and build it in PhoneGap :/

Justin, I'm able to run the app just fine with the above code.

Try hard coding a lat / long in and tell me what happens.

//GreenLand 
let forecastURL = NSURL(string: "\(72.371224,-41.382676)", relativeToURL:baseURL)

If you are still getting the nil error message after this.. then the forecastURL is not the issue..

Note: As long as you are using 6.1.1 then you will not be effected by the changes apple has made to the swift language.

Justin Bane
Justin Bane
7,850 Points

Aaron, hard coding it in the way you show I get the same "fatal error: unexpectedly found nil while unwrapping an Optional value" But if I hard code it the way the lesson has (of course) it works great "72.371224,-41.382676" without the interpolation

So I guess my question now is what is wrong with this code? This is the chunk that is always failing -= I am able to print valid values EVERYWHERE - but this section always fails in trying to pass it dynamically:

func getCurrentWeatherData() -> Void {

        var locationString: String = ""
        var lat = "\(currentLocation.coordinate.latitude)"
        var lon = "\(currentLocation.coordinate.longitude)"

        locationString = "\(lat), \(lon)"

        let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/")
        let forecastURL = NSURL(string: "\(locationString)", relativeToURL: baseURL)
        println(forecastURL)

        let sharedSession = NSURLSession.sharedSession()
        let downloadTask: NSURLSessionDownloadTask =
        sharedSession.downloadTaskWithURL(forecastURL!, completionHandler: {
            (location: NSURL!, response: NSURLResponse!, error: NSError!) -> Void in

Also - if I println the forcastURL (as you can see in my code - it returns nil