Forced Unwrapping

Still struggling with forced unwrapping , any help appreciated.

self.latitude1 = self.locationManager.location?.coordinate.latitude as! Double

I get this error:

Forced cast from 'CLLocationDegrees?' (aka 'Optional') to 'Double' only unwraps optionals

Thank you

CLLocationDegrees is defined in CoreLocation as

typealias CLLocationDegrees = Double

so force casting a value of type CLLocationDegrees? to Double is the same as casting a value of type Double? to Double. That is, you can more clearly represent the operation you're performing as:

self.latitude1 = self.locationManager.location?.coordinate.latitude!

since it's just a force unwrap.

Thank you

When I try that it just keeps bouncing me back to my original setup:

Cannot assign value of type 'CLLocationDegrees?' (aka 'Optional') to type 'Double'
Insert ' as! Double'
Cannot force unwrap value of non-optional type 'CLLocationDegrees' (aka 'Double')

Ah, sorry—because the member chain involves optional chaining (.location?), you actually want to write:

self.latitude1 = (self.locationManager.location?.coordinate.latitude)!

The reason for this is that when you write it how I originally did (without the parentheses) the compiler is basically doing:

var latitude: Double?
if let location = self.locationManager.location {
  latitude = location.coordinate.latitude!
}
self.latitude1 = latitude

Since latitude is non-optional on CLLocationCoordinate2D, that code is nonsense. With the parentheses, the compiler does something like:

var latitude: Double?
if let location = self.locationManager.location {
  latitude = location.coordinate.latitude
}
self.latitude1 = latitude!

Note that these code transformations are not something that actually happens, they're just meant to illustrate the difference in behavior between the two examples.

Beautiful .... Thank you :-)

Since there's no case for location returning nil and succeed anyway, you might as well do:

self.latitude1 = self.locationManager.location!.coordinate.latitude!

which more-or-less says that location won't be nil, and latitude won't be nil

3 Likes

I have a meta-commentary for this question. When I first started writing Swift, in early 2015, I kept inserting !s into my code until it compiled. I have come to believe that force-unwrapping should be avoided except in two specific cases: IBOutlets and (influenced by Jon Reid) properties that are guaranteed to be set up in unit tests. Rather than force-unwrapping, I now either provide default values with ?? or, if operation can't continue without a successful unwrapping, fatalError() with a message that describes why I expected unwrapping to succeed or why the unwrap failed.

In your case, the code might look like:

guard let location = self.locationManager.location else {
  fatalError("locationManager.location was nil.")
}

guar let latitude = location.coordinate.latitude else {
  fatalError("coordinate.latitude was nil.")
}

self.latitude1 = latitude

Default values make the code more robust, and fatalError() messages document for code readers my expectations of successful unwrapping. I wrote more about this stylistic approach here.

1 Like

I think it's better to put them in a larger box of things that are provably not nil, which would include some other scenarios where guard let will just be dead code.

1 Like

Even just:

self.latitude1 = self.locationManager.location!.coordinate.latitude

Thank you, That looks clean but when I use that I get the following error Initializer for conditional binding must have Optional type, not 'CLLocationDegrees' (aka 'Double')

This is the same thing I was getting at in my previous post—CLLocationCoordinate2D.latitude is non-optional, so once you have somehow unwrapped self.locationManager.location (using guard let, !, etc.), you won't need to unwrap location.coordinate.latitude at all.

Sorry but isn't that what I am doing here?:

uard let location = self.locationManager.location else {
fatalError("locationManager.location was nil.")
}
guard let latitude = location.coordinate.latitude else {
fatalError("coordinate.latitude was nil.")
}
self.latitude1 = latitude

I'm saying that you don't need the second guard let—it should work fine to do

guard let location = self.locationManager.location else {
  fatalError("locationManager.location was nil.")
}

self.latitude1 = location.coordinate.latitude
1 Like

Ahhhh yes that works thank you very much.

1 Like

Here're a few links about optional chaining that you may find helpful:

2 Likes