Have an app I wrote that I needed altitude from. Remarkably, searches for altitude in the documentation for iOS turns up very little. It‘s a property in CLLocation, and it is fetched by the LocationManager, but for whatever reason, it seemed it was always 0. The course to solving this mystery is worth documenting because it touches on a lot of pieces that have been covered here before: documentation, heuristics, the process of coding by just filling in blanks rather than following logical procedural pathways, etc. Along the way, there are also some very interesting design considerations.
First, when you figure out that you are not getting something from a framework, what‘s the first thing to do? Of course, check the documentation. I didn‘t do that. I searched and of course ended up at StackOverflow. However, that led to two threads that seemed like they might hold some clues. Especially the second one I found seemed to indicate that to get the altitude you had to ask for Best Accuracy, which would engage a greater effort from the Location Manager.
Once I had this piece of information, I felt I could go do something to test this out. An interesting thing happened at this point: I decided to make a simple test project instead of add it to the codebase. But then, it occurred to me to go find an existing example.
I have groused about this before: it‘s unbelievable to me that the documentation on the Apple site is not better tagged and indexed. I navigated to that part of the site that had just example code and searched for location or CLLocation, whatever, and found 4 or 5 examples. The LocateMe one I remembered, and downloaded it and quickly was looking at the code. I remembered it: it sets up a property to hold the best location it‘s gotten, then loops until the accuracy is as good as what you are asking for, then drops out and returns it.
The good news was that it did get the altitude, but, when I went back to my own code, I was still getting zero. Here‘s where things got pretty ugly and stupid. The LocateMe example is actually kind of pernicious. It looks like an innocent little puppy dog of an app, and operationally it is straightforward, but there are some deceptive aspects that make things kind of stupid. The worst is that the line that checks whether the accuracy is sufficient will never trigger if you ask for best. Why? Because best is a constant with a value of negative 1. Only an invalid location would have negative 1, but it would never get to that place because an earlier block would have detected its being invalid. Now, in a weird kind of a way, you can imagine that this is actually kind of good design because it is saying best. Right? So you got the best because you got whatever you were able to get given the time you were willing to wait (more on that).
Eventually, what I figured out was that the altitude was there in the CLLocation but later, in the Address class (my own), it was 0. How on earth could this be happening? Well, of course, there was another step involved: reverse geocoding. Sure enough, the location was handed to the reverse geocoder, which in turn, returned a place mark, and though the MKPlacemark class has an altitude attribute, it was 0. Kind of makes sense. But it also points up some classic perniciousness in programming: that we model things as independent, but then, when there are natural compound behaviors, e.g. the path from a device to a set of coordinates to an address requires both the derivation of a location and then an address, the door is left open for those mappings to seep out on us. But design considerations don‘t stop there: why are we even writing code for determining which is the best? This would be a great async idiom to have at the ready: combining the collection of a number of progressively refined results from any source and then retaining the best one. Furthermore, what constitutes best? The assumption in the API is that it will always be accuracy. In my case, it was soon as I had a location with an altitude I‘d have been ready to go. Wouldn‘t it make sense to let me just provide a block that could be triggered and when it returned true, we would drop out and the current result would be passed to the delegate? This would require a slightly more elaborate design, but it would be very readable, and there would be many ways to get desired results without a lot of trouble.
Recap: the path to figuring out how to get altitude in the first place was tortured. Note to self: always look for some example code quickly. But then, when the example introduces a bunch of other vagaries, it might confuse matters. Ultimately, these are very interesting idioms that we are going to see a lot more of. People think of concurrency as an optimization sideshow that most need not fiddle with until their site has millions of visitors. In fact, as the Location Manager attests, inversion of control and concurrency are required the minute you find yourself depending upon a service that you don‘t control. It‘s pretty hard to imagine much serious code in the Age of the Cloud, that will be free of those constraints..