chemistree apps :: lab notes 2018-09-22T12:06:58-06:00 http://chemistreeapps.com Chemistree Apps Copyright (c) 2018 Chemistree Apps LLC 'If let' and thread safety 2014-09-16T00:00:00-06:00 http://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&#39;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 Geocoding 2012-01-23T00:00:00-07:00 http://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 &lt;CoreLocation/CoreLocation.h&gt;</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">&gt;</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">@&quot;Found placemark: %@&quot;</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">@&quot;Outer Mongolia&quot;</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 @ &lt;+47.00000000,+103.00000000&gt; +/- 100.00m, region (identifier &lt;+47.00019850,+103.00094600&gt; radius 129.34) &lt;+47.00019850,+103.00094600&gt; 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 @ &lt;+61.74309935,+22.56331008&gt; +/- 100.00m, region (identifier &lt;+61.74316400,+22.57415800&gt; radius 9380.53) &lt;+61.74316400,+22.57415800&gt; 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">@&quot;Found placemark: %@&quot;</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">@&quot;38720&quot;</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 @ &lt;+61.74309935,+22.56331008&gt; +/- 100.00m, region (identifier &lt;+61.74316400,+22.57415800&gt; radius 9380.53) &lt;+61.74316400,+22.57415800&gt; radius 9380.53m Forward Geocoding[8216:f803] Found placemark: Duncan, MS 38720, United States @ &lt;+34.11106550,-90.82042082&gt; +/- 100.00m, region (identifier &lt;+34.10980250,-90.82122800&gt; radius 15440.36) &lt;+34.10980250,-90.82122800&gt; radius 15440.36m Forward Geocoding[8216:f803] Found placemark: 38720 San Andrés y Sauces (Canary Islands), Spain @ &lt;+28.77990090,-17.79729925&gt; +/- 100.00m, region (identifier &lt;+28.78246300,-17.79785150&gt; radius 6970.58) &lt;+28.78246300,-17.79785150&gt; 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 @ &lt;+34.11106550,-90.82042082&gt; +/- 100.00m, region (identifier &lt;+34.10980250,-90.82122800&gt; radius 15440.36) &lt;+34.10980250,-90.82122800&gt; 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>