chemistree apps :: lab notes2018-09-22T12:06:58-06:00http://chemistreeapps.comChemistree AppsCopyright (c) 2018 Chemistree Apps LLC'If let' and thread safety2014-09-16T00:00:00-06:00http://chemistreeapps.com/labnotes/2014/09/16/if-let/<p>I have a confession to make. The first time I saw <code>if let</code> syntax, I thought it was ugly, complex, and confusing. However, my opinion has changed, and I wanted to share some of my favorite things about it.</p>
<p>One major advantage to using <code>if let</code> is that it is naturally defensive against threading issues. When I starting using Swift, I saw a lot of third-party code online written like this, and was even tempted to adopt the style myself:</p>
<div class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">var</span> <span class="nl">postTitle</span><span class="p">:</span> <span class="n">String</span><span class="o">?</span>
<span class="k">func</span> <span class="n">doSomethingWithAnOptionalValue</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">postTitle</span> <span class="o">==</span> <span class="nb">nil</span> <span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="n">doStuffWith</span><span class="p">(</span><span class="n">postTitle</span><span class="o">!</span><span class="p">)</span>
<span class="p">}</span></code></pre></div>
<p>I can understand the appeal. While the indentation seems tame here, it can get pretty gnarly in more complex examples. The approach above avoids seemingly needless indentation, the creation of a local constant that you need to worry about, etc. Can you see the problem with that approach, however?</p>
<div class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">func</span> <span class="nf">doSomethingWithAnOptionalValue</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">postTitle</span> <span class="o">==</span> <span class="nb">nil</span> <span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="c1">// Let's say postTitle just got assigned nil on a different thread</span>
<span class="n">doStuffWith</span><span class="p">(</span><span class="n">postTitle</span><span class="o">!</span><span class="p">)</span> <span class="c1">// Crash</span>
<span class="p">}</span></code></pre></div>
<p>Yeah, that’s a problem. This brings up a different point - Apple made the ‘forced upwrapping’ operator <code>!</code> for a reason. It should be something carefully considered, and avoided whenever possible.</p>
<p>In contrast, consider the <code>if let</code> approach:</p>
<div class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">func</span> <span class="nf">doSomethingWithAnOptionalValue</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="n">unwrappedPostTitle</span> <span class="o">=</span> <span class="n">postTitle</span> <span class="p">{</span>
<span class="n">doStuffWith</span><span class="p">(</span><span class="n">unwrappedPostTitle</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>If the variable got assigned <code>nil</code> or to another value while we’re inside the if statement, it doesn’t matter. We already have our constant reference pointing to the object the instance variable referred to when the <code>if let</code> was evaluated, and we already know it’s not <code>nil</code>.</p>
<p>I also got annoyed by the funky names I had to come up with for the unwrapped optional, as above. Luckily, Swift solves that for us with its (impressively flexible) scoping:</p>
<div class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">func</span> <span class="nf">doSomethingWithAnOptionalValue</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="n">postTitle</span> <span class="o">=</span> <span class="n">postTitle</span> <span class="p">{</span>
<span class="n">doStuffWith</span><span class="p">(</span><span class="n">postTitle</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p><code>postTitle</code> is now scoped to this if statement, so <code>postTitle</code> and <code>self.postTitle</code> are now two entirely different things.</p>
Forward Geocoding2012-01-23T00:00:00-07:00http://chemistreeapps.com/labnotes/2012/01/23/forward-geocoding/<p>In iOS 5, Apple introduced a great new feature: forward geocoding. Forward here is as opposed to reverse geocoding, which has been present since MapKit was introduced. Where reverse geocoding takes a longitude/latitude pair and converts it to city, state, zip code and so on, forward geocoding takes a city, state, zip code kind of thing and turns it into a lat/long.</p>
<h3 id="first-try">First try</h3>
<p>Here’s a simple snippet to try forward geocoding on iOS 5. Make sure you have CoreLocation.framework in your Frameworks section, then include the header:</p>
<div class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="cp">#import <CoreLocation/CoreLocation.h></span></code></pre></div>
<p>Then, to test it out:</p>
<div class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="bp">CLGeocoder</span> <span class="o">*</span><span class="n">geocoder</span> <span class="o">=</span> <span class="p">[[</span><span class="bp">CLGeocoder</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>
<span class="n">CLGeocodeCompletionHandler</span> <span class="n">completionHandler</span> <span class="o">=</span>
<span class="o">^</span><span class="p">(</span><span class="bp">NSArray</span> <span class="o">*</span><span class="n">placemarks</span><span class="p">,</span> <span class="bp">NSError</span> <span class="o">*</span><span class="n">error</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">([</span><span class="n">placemarks</span> <span class="n">count</span><span class="p">]</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="bp">CLPlacemark</span> <span class="o">*</span><span class="n">placemark</span> <span class="o">=</span> <span class="p">[</span><span class="n">placemarks</span> <span class="nl">objectAtIndex</span><span class="p">:</span><span class="mi">0</span><span class="p">];</span>
<span class="n">NSLog</span><span class="p">(</span><span class="s">@"Found placemark: %@"</span><span class="p">,</span> <span class="n">placemark</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">[</span><span class="n">geocoder</span> <span class="nl">geocodeAddressString</span><span class="p">:</span><span class="s">@"Outer Mongolia"</span> <span class="nl">completionHandler</span><span class="p">:</span><span class="n">completionHandler</span><span class="p">];</span></code></pre></div>
<p><em>Note: For brevity, this snippet leaks <code>geocoder</code> if not run on ARC. The sample code provided releases it correctly.</em></p>
<p>If you run it in the iOS 5 simulator (or device), it will work great. Turns out Outer Mongolia is really a place:</p>
<pre><code>Forward Geocoding[1902:f803] Found placemark: Outer Mongolia, Mongolia @ <+47.00000000,+103.00000000> +/- 100.00m, region (identifier <+47.00019850,+103.00094600> radius 129.34) <+47.00019850,+103.00094600> radius 129.34m
</code></pre>
<h3 id="warnings">Warnings</h3>
<p>It’s great that we can specify location information with such free syntax. However, with that great power comes great responsibility. Let’s say you have a perfectly normal-sounding US zip code, <code>38720</code>. That zip code belongs to Duncan, MS. However, when we run it through Core Location, we get this:</p>
<pre><code>Forward Geocoding[7123:f803] Found placemark: 38720 Kankaanpaa, Finland @ <+61.74309935,+22.56331008> +/- 100.00m, region (identifier <+61.74316400,+22.57415800> radius 9380.53) <+61.74316400,+22.57415800> radius 9380.53m
</code></pre>
<p>That’s in Finland, folks. Not quite what we were looking for. However, we do get an array of placemarks back - perhaps it’s somewhere in the array, just not the first object?</p>
<div class="highlight"><pre><code class="language-objectivec" data-lang="objectivec"><span class="bp">CLGeocoder</span> <span class="o">*</span><span class="n">geocoder</span> <span class="o">=</span> <span class="p">[[</span><span class="bp">CLGeocoder</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>
<span class="n">CLGeocodeCompletionHandler</span> <span class="n">completionHandler</span> <span class="o">=</span>
<span class="o">^</span><span class="p">(</span><span class="bp">NSArray</span> <span class="o">*</span><span class="n">placemarks</span><span class="p">,</span> <span class="bp">NSError</span> <span class="o">*</span><span class="n">error</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="bp">CLPlacemark</span> <span class="o">*</span><span class="n">placemark</span> <span class="k">in</span> <span class="n">placemarks</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">NSLog</span><span class="p">(</span><span class="s">@"Found placemark: %@"</span><span class="p">,</span> <span class="n">placemark</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">[</span><span class="n">geocoder</span> <span class="nl">geocodeAddressString</span><span class="p">:</span><span class="s">@"38720"</span> <span class="nl">completionHandler</span><span class="p">:</span><span class="n">completionHandler</span><span class="p">];</span></code></pre></div>
<pre><code>Forward Geocoding[8216:f803] Found placemark: 38720 Kankaanpaa, Finland @ <+61.74309935,+22.56331008> +/- 100.00m, region (identifier <+61.74316400,+22.57415800> radius 9380.53) <+61.74316400,+22.57415800> radius 9380.53m
Forward Geocoding[8216:f803] Found placemark: Duncan, MS 38720, United States @ <+34.11106550,-90.82042082> +/- 100.00m, region (identifier <+34.10980250,-90.82122800> radius 15440.36) <+34.10980250,-90.82122800> radius 15440.36m
Forward Geocoding[8216:f803] Found placemark: 38720 San Andrés y Sauces (Canary Islands), Spain @ <+28.77990090,-17.79729925> +/- 100.00m, region (identifier <+28.78246300,-17.79785150> radius 6970.58) <+28.78246300,-17.79785150> radius 6970.58m
</code></pre>
<p>Okay, so it found it after all - it’s the second item in the array, not the first. We can just filter out the non-US results and we’ll be fine. However, why not let Core Location do that for us? Instead, let’s be more specific and use <code>38720 United States</code>:</p>
<pre><code>Forward Geocoding[7214:f803] Found placemark: Duncan, MS 38720, United States @ <+34.11106550,-90.82042082> +/- 100.00m, region (identifier <+34.10980250,-90.82122800> radius 15440.36) <+34.10980250,-90.82122800> radius 15440.36m
</code></pre>
<p>Ahh, there we go. The moral here is to be careful and specify as much information as you have. I learned that one the hard way.</p>
<h3 id="legacy-os-versions">Legacy OS versions</h3>
<p>Unfortunately, when you run this code on iOS 4, you get no output. As mentioned above, forward geocoding is an iOS 5 feature. If you need it on iOS versions earlier than 5, there are a few options, including:</p>
<ol>
<li>Third-party geocoding services</li>
<li>In-storage geocoding database</li>
</ol>
<h3 id="conclusion">Conclusion</h3>
<p>Where might this be useful? Beyond the obvious uses, like allowing a user to input a zip code or city/state name and having it come back with locations when your server only supports lat/long searching, you can now display city details rather than just “Current Location”.</p>
<p>Go forth and geocode!</p>
<div class="spacer"> </div>
<div class="chemistreeExperiment">
<a href="/experiments/ForwardGeocoding.zip">
<p text-align="center">Download Experiment</p>
<img src="/project.png" />
</a>
</div>