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 trialJustin Bane
7,850 PointsCoreLocation: 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
7,850 PointsARRRRGGGGHHHH!
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
Treehouse TeacherSeems 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)"
}
agreatdaytocode
24,757 PointsHere 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
7,850 PointsYour 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 :/
agreatdaytocode
24,757 PointsJustin, 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
7,850 PointsAaron, 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