Jekyll2022-09-25T19:26:32+01:00https://rcharlton.github.io/feed.xmlBranch◦PredictionThoughts and ramblings about Swift, iOS development and other things.Robin CharltonExperimenting With AsyncStream2022-09-25T10:00:00+01:002022-09-25T10:00:00+01:00https://rcharlton.github.io/blog/post-experimenting-with-async-stream<p><code class="language-plaintext highlighter-rouge">AsyncSequence</code> and <code class="language-plaintext highlighter-rouge">AsyncStream</code> are mechanisms to model an asynchronous stream of values using Swift’s native structured concurrency. As a fan of functional reactive programming and functional composition but one who winces at the syntactic complexity of Combine or Rx then this has a lot of appeal.</p>
<p>This post is a brain dump that documents an afternoon spent noodling about with <code class="language-plaintext highlighter-rouge">CLLocationManager</code> and <code class="language-plaintext highlighter-rouge">AsyncStream</code>. Credit to Andy Ibanez who’s comprehensive <a href="https://www.andyibanez.com/posts/understanding-actors-in-the-new-concurrency-model-in-swift/">post</a> on concurrency inspired this effort.</p>
<p>The goal was to write code like:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">altitudes</span> <span class="o">=</span> <span class="nf">locations</span><span class="p">()</span>
<span class="o">.</span><span class="n">filter</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">verticalAccuracy</span> <span class="o"><</span> <span class="n">kCLLocationAccuracyNearestTenMeters</span> <span class="p">}</span>
<span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">altitude</span> <span class="p">}</span>
<span class="k">for</span> <span class="n">await</span> <span class="n">altitude</span> <span class="k">in</span> <span class="n">altitudes</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">altitude</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is achieved with the following function. The use of a function rather than e.g. a wrapper class is to eliminate shared state (within my code at least - who knows what Apple is doing?!) and reduce the potential for side-effects.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// CLLocationManager doesn't appear to function correctly off the main thread.</span>
<span class="kd">@MainActor</span>
<span class="kd">func</span> <span class="nf">locations</span><span class="p">()</span> <span class="o">-></span> <span class="kt">AsyncStream</span><span class="o"><</span><span class="kt">CLLocation</span><span class="o">></span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">locationManager</span> <span class="o">=</span> <span class="kt">CLLocationManager</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">locationHandler</span> <span class="o">=</span> <span class="kt">LocationHandler</span><span class="p">()</span>
<span class="n">locationManager</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">locationHandler</span>
<span class="k">var</span> <span class="nv">shouldStart</span> <span class="o">=</span> <span class="kc">false</span>
<span class="k">var</span> <span class="nv">continuation</span><span class="p">:</span> <span class="kt">AsyncStream</span><span class="o"><</span><span class="kt">CLLocation</span><span class="o">>.</span><span class="kt">Continuation</span><span class="p">?</span>
<span class="n">locationHandler</span><span class="o">.</span><span class="n">didChangeAuthorization</span> <span class="o">=</span> <span class="p">{</span> <span class="n">locationManager</span> <span class="k">in</span>
<span class="k">if</span> <span class="n">locationManager</span><span class="o">.</span><span class="n">authorizationStatus</span> <span class="o">==</span> <span class="o">.</span><span class="n">denied</span> <span class="p">{</span>
<span class="n">continuation</span><span class="p">?</span><span class="o">.</span><span class="nf">finish</span><span class="p">()</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">shouldStart</span> <span class="p">{</span>
<span class="n">shouldStart</span> <span class="o">=</span> <span class="kc">false</span>
<span class="n">locationManager</span><span class="o">.</span><span class="nf">startUpdatingLocation</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">locationHandler</span><span class="o">.</span><span class="n">didFailWithError</span> <span class="o">=</span> <span class="p">{</span> <span class="n">locationManager</span><span class="p">,</span> <span class="n">error</span> <span class="k">in</span>
<span class="k">if</span> <span class="n">locationManager</span><span class="o">.</span><span class="n">authorizationStatus</span> <span class="o">!=</span> <span class="o">.</span><span class="n">notDetermined</span> <span class="p">{</span>
<span class="n">continuation</span><span class="p">?</span><span class="o">.</span><span class="nf">finish</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">locationHandler</span><span class="o">.</span><span class="n">didUpdateLocations</span> <span class="o">=</span> <span class="p">{</span> <span class="n">locationManager</span><span class="p">,</span> <span class="n">locations</span> <span class="k">in</span>
<span class="n">locations</span><span class="o">.</span><span class="n">forEach</span> <span class="p">{</span> <span class="n">continuation</span><span class="p">?</span><span class="o">.</span><span class="nf">yield</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kt">AsyncStream</span><span class="o"><</span><span class="kt">CLLocation</span><span class="o">></span> <span class="p">{</span>
<span class="n">continuation</span> <span class="o">=</span> <span class="nv">$0</span>
<span class="nv">$0</span><span class="o">.</span><span class="n">onTermination</span> <span class="o">=</span> <span class="p">{</span> <span class="kd">@Sendable</span> <span class="n">_</span> <span class="k">in</span>
<span class="c1">// Use this escaping closure to retain these objects whilst the stream is active.</span>
<span class="c1">// This appears to break the Sendable constraint since these reference types are not isolated.</span>
<span class="n">_</span> <span class="o">=</span> <span class="n">locationHandler</span>
<span class="n">_</span> <span class="o">=</span> <span class="n">locationManager</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">locationManager</span><span class="o">.</span><span class="n">authorizationStatus</span> <span class="o">==</span> <span class="o">.</span><span class="n">notDetermined</span> <span class="p">{</span>
<span class="n">shouldStart</span> <span class="o">=</span> <span class="kc">true</span>
<span class="n">locationManager</span><span class="o">.</span><span class="nf">requestWhenInUseAuthorization</span><span class="p">()</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">locationManager</span><span class="o">.</span><span class="nf">startUpdatingLocation</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// MARK: -</span>
<span class="kd">private</span> <span class="kd">class</span> <span class="kt">LocationHandler</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">CLLocationManagerDelegate</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">didChangeAuthorization</span><span class="p">:</span> <span class="p">(</span><span class="kt">CLLocationManager</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">didFailWithError</span><span class="p">:</span> <span class="p">(</span><span class="kt">CLLocationManager</span><span class="p">,</span> <span class="kt">Error</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
<span class="k">var</span> <span class="nv">didUpdateLocations</span><span class="p">:</span> <span class="p">(</span><span class="kt">CLLocationManager</span><span class="p">,</span> <span class="p">[</span><span class="kt">CLLocation</span><span class="p">])</span> <span class="o">-></span> <span class="kt">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
<span class="k">override</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="kd">deinit</span> <span class="p">{</span>
<span class="nf">debugPrint</span><span class="p">(</span><span class="nf">type</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="k">self</span><span class="p">),</span> <span class="kd">#function</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">locationManagerDidChangeAuthorization</span><span class="p">(</span><span class="n">_</span> <span class="nv">manager</span><span class="p">:</span> <span class="kt">CLLocationManager</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">didChangeAuthorization</span><span class="p">(</span><span class="n">manager</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">locationManager</span><span class="p">(</span><span class="n">_</span> <span class="nv">manager</span><span class="p">:</span> <span class="kt">CLLocationManager</span><span class="p">,</span> <span class="n">didFailWithError</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">Error</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">didFailWithError</span><span class="p">(</span><span class="n">manager</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">locationManager</span><span class="p">(</span><span class="n">_</span> <span class="nv">manager</span><span class="p">:</span> <span class="kt">CLLocationManager</span><span class="p">,</span> <span class="n">didUpdateLocations</span> <span class="nv">locations</span><span class="p">:</span> <span class="p">[</span><span class="kt">CLLocation</span><span class="p">])</span> <span class="p">{</span>
<span class="nf">didUpdateLocations</span><span class="p">(</span><span class="n">manager</span><span class="p">,</span> <span class="n">locations</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The above really isn’t intended to be production code.</p>
<p>Suggestions for future work include:</p>
<ul>
<li>Replacing the use of <code class="language-plaintext highlighter-rouge">CLLocation</code> with an immutable value type.</li>
<li>Devising a mechanism to throw errors; a rather severe limitation of <code class="language-plaintext highlighter-rouge">AsyncStream</code> when compared with Combine or Rx.</li>
<li>Investigate the behaviour of awaiting streams when the containing <code class="language-plaintext highlighter-rouge">Task</code> is cancelled.</li>
</ul>Robin CharltonStreaming locations in modern SwiftUIColor + Transformed2019-02-16T09:00:00+00:002021-01-09T21:20:02+00:00https://rcharlton.github.io/blog/post-uicolor-transformed<p>I really enjoyed reading Erik D. Kennedy’s <a href="https://medium.com/@erikdkennedy/color-in-ui-design-a-practical-framework-e18cacd97f9e">post</a> on Color in UI Design. In it he discusses how the ability to modify one base colour into many variations is a fundamental skill in colouring interface designs. Erik examines colours in the real world and shows how hue, saturation and brightness vary in different lighting conditions.</p>
<p><a href="https://medium.com/@erikdkennedy/color-in-ui-design-a-practical-framework-e18cacd97f9e">Color in UI Design: A (Practical) Framework – Erik D. Kennedy – Medium</a></p>
<p>He observes how darker colour variations have higher saturation and lower brightness. But if saturation and brightness are unchanged, shifting hue towards red, green or blue will decrease the luminosity, or perceived lightness of the colour. And shifting the hue towards yellow, cyan, or magenta will increase the perceived lightness of the colour.</p>
<p><img src="/assets/images/posts/post-uicolor-transformed/jon-flobrant-229724-unsplash.jpg" alt="Tree shadow image" title="Photo by Jon Flobrant on Unsplash" height="500px" width="400px" /></p>
<p><img src="/assets/images/posts/post-uicolor-transformed/thanos-pal-1146444-unsplash.jpg" alt="Door image" title="Photo by Thanos Pal on Unsplash" height="500px" width="400px" /></p>
<p>Armed with this knowledge it seems impossible to resist writing an extension on <code class="language-plaintext highlighter-rouge">UIColor</code> to encode this.</p>
<h3 id="uicolortransformedswift">UIColor+Transformed.swift</h3>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIColor</span> <span class="p">{</span>
<span class="kd">struct</span> <span class="kt">Transform</span> <span class="p">{</span>
<span class="c1">/// The offset applied to hue, in degrees 0...+-360.</span>
<span class="k">let</span> <span class="nv">hue</span><span class="p">:</span> <span class="kt">CGFloat</span>
<span class="c1">/// The % change to the saturation.</span>
<span class="k">let</span> <span class="nv">saturation</span><span class="p">:</span> <span class="kt">CGFloat</span>
<span class="c1">/// The % change to the brightness.</span>
<span class="k">let</span> <span class="nv">brightness</span><span class="p">:</span> <span class="kt">CGFloat</span>
<span class="c1">/// color.transformed(by: identity) is always equal to color.</span>
<span class="kd">static</span> <span class="k">let</span> <span class="nv">identity</span> <span class="o">=</span> <span class="kt">Transform</span><span class="p">(</span><span class="nv">hue</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">saturation</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">brightness</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Compute the color formed by applying the given transform to this color.</span>
<span class="c1">///</span>
<span class="c1">/// - Parameter transform: Describes the changes to the color.</span>
<span class="c1">/// - Returns: The resulting colour.</span>
<span class="kd">func</span> <span class="nf">transformed</span><span class="p">(</span><span class="n">by</span> <span class="nv">transform</span><span class="p">:</span> <span class="kt">Transform</span><span class="p">)</span> <span class="o">-></span> <span class="kt">UIColor</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">hsba</span> <span class="o">=</span> <span class="kt">UnsafeMutablePointer</span><span class="o"><</span><span class="kt">CGFloat</span><span class="o">>.</span><span class="nf">allocate</span><span class="p">(</span><span class="nv">capacity</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span>
<span class="n">hsba</span><span class="o">.</span><span class="nf">initialize</span><span class="p">(</span><span class="nv">repeating</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span> <span class="nv">count</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span>
<span class="nf">getHue</span><span class="p">(</span><span class="o">&</span><span class="n">hsba</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nv">saturation</span><span class="p">:</span> <span class="o">&</span><span class="n">hsba</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="nv">brightness</span><span class="p">:</span> <span class="o">&</span><span class="n">hsba</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="nv">alpha</span><span class="p">:</span> <span class="o">&</span><span class="n">hsba</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
<span class="k">let</span> <span class="nv">phase</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">(</span><span class="mi">6</span> <span class="o">*</span> <span class="n">hsba</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">let</span> <span class="nv">hueDirection</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="p">((</span><span class="n">phase</span> <span class="o">%</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">?</span> <span class="mf">1.0</span> <span class="p">:</span> <span class="o">-</span><span class="mf">1.0</span>
<span class="k">let</span> <span class="nv">hue</span> <span class="o">=</span> <span class="p">(</span><span class="n">hsba</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="p">(</span><span class="n">hueDirection</span> <span class="o">*</span> <span class="n">transform</span><span class="o">.</span><span class="n">hue</span> <span class="o">/</span> <span class="mi">360</span><span class="p">))</span><span class="o">.</span><span class="nf">clamped</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="mi">0</span><span class="o">...</span><span class="mi">1</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">saturation</span> <span class="o">=</span> <span class="p">(</span><span class="n">hsba</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">transform</span><span class="o">.</span><span class="n">saturation</span><span class="p">)</span><span class="o">.</span><span class="nf">clamped</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="mi">0</span><span class="o">...</span><span class="mi">1</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">brightness</span> <span class="o">=</span> <span class="p">(</span><span class="n">hsba</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="n">transform</span><span class="o">.</span><span class="n">brightness</span><span class="p">)</span><span class="o">.</span><span class="nf">clamped</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="mi">0</span><span class="o">...</span><span class="mi">1</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="p">(</span><span class="nv">hue</span><span class="p">:</span> <span class="n">hue</span><span class="p">,</span> <span class="nv">saturation</span><span class="p">:</span> <span class="n">saturation</span><span class="p">,</span> <span class="nv">brightness</span><span class="p">:</span> <span class="n">brightness</span><span class="p">,</span> <span class="nv">alpha</span><span class="p">:</span> <span class="n">hsba</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
<span class="n">hsba</span><span class="o">.</span><span class="nf">deinitialize</span><span class="p">(</span><span class="nv">count</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span>
<span class="n">hsba</span><span class="o">.</span><span class="nf">deallocate</span><span class="p">()</span>
<span class="k">return</span> <span class="n">result</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">private</span> <span class="kd">extension</span> <span class="kt">Comparable</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">clamped</span><span class="p">(</span><span class="n">to</span> <span class="nv">range</span><span class="p">:</span> <span class="kt">ClosedRange</span><span class="o"><</span><span class="k">Self</span><span class="o">></span><span class="p">)</span> <span class="o">-></span> <span class="k">Self</span> <span class="p">{</span>
<span class="k">return</span> <span class="nf">max</span><span class="p">(</span><span class="nf">min</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">range</span><span class="o">.</span><span class="n">upperBound</span><span class="p">),</span> <span class="n">range</span><span class="o">.</span><span class="n">lowerBound</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>With this we can define some colour transformations and apply them to the base colours in our app’s UI.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">baseColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="p">(</span><span class="nv">red</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span> <span class="nv">green</span><span class="p">:</span> <span class="mf">0.3</span><span class="p">,</span> <span class="nv">blue</span><span class="p">:</span> <span class="mf">0.4</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">darkerTransform</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="kt">Transform</span><span class="p">(</span><span class="nv">hue</span><span class="p">:</span> <span class="o">+</span><span class="mi">6</span><span class="p">,</span> <span class="nv">saturation</span><span class="p">:</span> <span class="o">+</span><span class="mf">0.2</span><span class="p">,</span> <span class="nv">brightness</span><span class="p">:</span> <span class="o">-</span><span class="mf">0.3</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">darkerColor</span> <span class="o">=</span> <span class="n">baseColor</span><span class="o">.</span><span class="nf">transformed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">darkerTransform</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">lighterTransform</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="kt">Transform</span><span class="p">(</span><span class="nv">hue</span><span class="p">:</span> <span class="o">-</span><span class="mi">6</span><span class="p">,</span> <span class="nv">saturation</span><span class="p">:</span> <span class="o">-</span><span class="mf">0.2</span><span class="p">,</span> <span class="nv">brightness</span><span class="p">:</span> <span class="o">+</span><span class="mf">0.3</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">lighterColor</span> <span class="o">=</span> <span class="n">baseColor</span><span class="o">.</span><span class="nf">transformed</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">lighterTransform</span><span class="p">)</span>
</code></pre></div></div>
<h3 id="example-app">Example App</h3>
<p>The screenshot below is from a simple example app that replicates the illustrations in Erik’s article.</p>
<p><img src="/assets/images/posts/post-uicolor-transformed/screenshot.png" alt="" /></p>
<p>The middle column is the base colour and automatically transformed lighter and variations are shown on either side. The latest version of this extension along with the full source code of the app is available <a href="https://github.com/rcharlton/ColorTransform">here</a>.</p>Robin CharltonExtensionImproving Collection View Data Sources2018-09-22T21:10:00+01:002018-09-22T21:10:00+01:00https://rcharlton.github.io/blog/post-improving-collection-view<p>Since <code class="language-plaintext highlighter-rouge">UICollectionView</code> arrived back in iOS 6.0 it’s become the workhorse of UI development. It can be seen everywhere rendering the dynamic content of the interweb’s feeds, lists and stories. Although capable the API suffers from that particular clunkiness that only an Objective-C Cocoa API can give 😆. This post presents a little syntactic sugar to improve the readability of our code and eliminate stupid programming mistakes.</p>
<p>Let’s look at a typical collection view example. First we register our cells:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">collectionView</span><span class="o">.</span><span class="nf">register</span><span class="p">(</span>
<span class="kt">MyCollectionViewCell</span><span class="o">.</span><span class="k">self</span><span class="p">,</span>
<span class="nv">forCellWithReuseIdentifier</span><span class="p">:</span> <span class="s">"MyCollectionViewCellIdentifier"</span>
<span class="p">)</span>
</code></pre></div></div>
<p>… and then consume those cells in the following data-source method:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">collectionView</span><span class="p">(</span>
<span class="n">_</span> <span class="nv">collectionView</span><span class="p">:</span> <span class="kt">UICollectionView</span><span class="p">,</span>
<span class="n">cellForItemAt</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">IndexPath</span>
<span class="p">)</span> <span class="o">-></span> <span class="kt">UICollectionViewCell</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">cell</span> <span class="o">=</span> <span class="n">collectionView</span><span class="o">.</span><span class="nf">dequeueReusableCell</span><span class="p">(</span>
<span class="nv">withReuseIdentifier</span><span class="p">:</span> <span class="s">"MyCollectionViewCellIdentifier"</span><span class="p">,</span>
<span class="nv">for</span><span class="p">:</span> <span class="n">indexPath</span>
<span class="p">)</span> <span class="k">as?</span> <span class="kt">MyCollectionViewCell</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">fatalError</span><span class="p">(</span><span class="s">"Gosh, this is embarressing 😭"</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">cell</span><span class="o">.</span><span class="nf">configure</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="nf">myData</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">indexPath</span><span class="p">))</span>
<span class="k">return</span> <span class="n">cell</span>
<span class="p">}</span>
</code></pre></div></div>
<p>What are the problems here? We must make an ad hoc association between the cell type <code class="language-plaintext highlighter-rouge">MyCollectionViewCell</code> and its string reuse identifier and then specify it in multiple places. Value identifiers like these are fragile as they can only be verified at run-time. There’s no way the compiler can catch a simple typo in a string.</p>
<p>Looking at <code class="language-plaintext highlighter-rouge">func collectionView(_ , cellForItemAt indexPath:)</code> we see that we downcast the cell to configure its subviews. Naturally this cast could fail if the cell were of an unexpected type and that leaves us with an <em>optional</em> <code class="language-plaintext highlighter-rouge">UICollectionViewCell</code> variable. Since the method itself returns a <em>non-optional</em> cell we’re left with a contract we cannot fulfil. We can’t return nil nor raise an exception and are left with no way to fail gracefully.</p>
<p>The path to improving this situation is by more formally expressing the relationship of the cell type with its reuse identifier.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">Reusable</span> <span class="p">{</span>
<span class="kd">static</span> <span class="k">var</span> <span class="nv">reuseIdentifier</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">MyCollectionViewCell</span><span class="p">:</span> <span class="kt">UICollectionViewCell</span><span class="p">,</span> <span class="kt">Reusable</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">configure</span><span class="p">(</span><span class="n">with</span> <span class="nv">data</span><span class="p">:</span> <span class="kt">MyData</span><span class="p">)</span> <span class="p">{</span>
<span class="o">...</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>With this in place we can extend <code class="language-plaintext highlighter-rouge">UICollectionView</code> and eliminate the manual specification of the reuse identifier for all <code class="language-plaintext highlighter-rouge">Reusable</code> conforming types:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UICollectionView</span> <span class="p">{</span>
<span class="kd">func</span> <span class="n">register</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">UICollectionViewCell</span><span class="o">></span><span class="p">(</span><span class="nv">_</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="k">Type</span><span class="p">)</span> <span class="k">where</span> <span class="kt">T</span><span class="p">:</span> <span class="kt">Reusable</span> <span class="p">{</span>
<span class="nf">register</span><span class="p">(</span><span class="kt">T</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">forCellWithReuseIdentifier</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="n">reuseIdentifier</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="n">dequeueReusableCell</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">UICollectionViewCell</span><span class="o">></span><span class="p">(</span><span class="k">for</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">)</span> <span class="o">-></span> <span class="kt">T</span> <span class="k">where</span> <span class="kt">T</span><span class="p">:</span> <span class="kt">Reusable</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">cell</span> <span class="o">=</span> <span class="nf">dequeueReusableCell</span><span class="p">(</span><span class="nv">withReuseIdentifier</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="n">reuseIdentifier</span><span class="p">,</span> <span class="nv">for</span><span class="p">:</span> <span class="n">indexPath</span><span class="p">)</span> <span class="k">as?</span> <span class="kt">T</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">fatalError</span><span class="p">(</span><span class="err">“</span><span class="kt">Failed</span> <span class="n">to</span> <span class="n">dequeue</span> <span class="nv">cell</span><span class="p">:</span> <span class="p">\(</span><span class="kt">T</span><span class="o">.</span><span class="n">reuseIdentifier</span><span class="p">)</span><span class="err">”</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">cell</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This leads to a greatly simplified call-site when both registering the cell type and consuming its instances:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">collectionView</span><span class="o">.</span><span class="nf">register</span><span class="p">(</span><span class="kt">MyCollectionViewCell</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">collectionView</span><span class="p">(</span>
<span class="n">_</span> <span class="nv">collectionView</span><span class="p">:</span> <span class="kt">UICollectionView</span><span class="p">,</span>
<span class="n">cellForItemAt</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">IndexPath</span>
<span class="p">)</span> <span class="o">-></span> <span class="kt">UICollectionViewCell</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">cell</span><span class="p">:</span> <span class="kt">MyCollectionViewCell</span> <span class="o">=</span> <span class="n">collectionView</span><span class="o">.</span><span class="nf">dequeueReusableCell</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">indexPath</span><span class="p">)</span>
<span class="n">cell</span><span class="o">.</span><span class="nf">configure</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="nf">myData</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">indexPath</span><span class="p">))</span>
<span class="k">return</span> <span class="n">cell</span>
<span class="p">}</span>
</code></pre></div></div>
<p>For most cases there’s little need to manually specify the reuse identifier. We can rely on Swift’s type information to supply this.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Reusable</span> <span class="p">{</span>
<span class="kd">static</span> <span class="k">var</span> <span class="nv">reuseIdentifier</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">String</span><span class="p">(</span><span class="nv">describing</span><span class="p">:</span> <span class="nf">type</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="k">self</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You may be wondering about that <code class="language-plaintext highlighter-rouge">fatalError</code> call in the new dequeue method. Whilst we can’t avoid the cell downcast it is now isolated to a single location rather than repeated in every <code class="language-plaintext highlighter-rouge">UICollectionViewDataSource</code> of our code-base. To actually eliminate the <code class="language-plaintext highlighter-rouge">fatalError</code> we must satisfying the method’s return type. This can be achieved by instantiating a new cell type to indicate the error rather than halting the app.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">dequeueReusableCell</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">UICollectionViewCell</span><span class="o">></span><span class="p">(</span><span class="k">for</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">)</span> <span class="o">-></span> <span class="kt">T</span> <span class="k">where</span> <span class="kt">T</span><span class="p">:</span> <span class="kt">Reusable</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">cell</span> <span class="o">=</span> <span class="nf">dequeueReusableCell</span><span class="p">(</span><span class="nv">withReuseIdentifier</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="n">reuseIdentifier</span><span class="p">,</span> <span class="nv">for</span><span class="p">:</span> <span class="n">indexPath</span><span class="p">)</span> <span class="k">as?</span> <span class="kt">T</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">MyErrorCollectionViewCell</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="s">"Failed to dequeue cell: </span><span class="se">\(</span><span class="kt">T</span><span class="o">.</span><span class="n">reuseIdentifier</span><span class="se">)</span><span class="s">"</span><span class="p">))</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">cell</span>
<span class="p">}</span>
</code></pre></div></div>Robin CharltonSyntactic SugarUsing Enums as Namespaces2018-08-06T10:00:00+01:002018-08-06T10:00:00+01:00https://rcharlton.github.io/blog/post-using-enums-as-namespaces<p>A few code-bases I’ve come across have used instance properties to hold constants, as follows.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">LoginViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
<span class="kd">private</span> <span class="k">let</span> <span class="nv">headerSpace</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">24</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is convenient since the value can be referenced within the class without any additional prefix or scoping.</p>
<p>However this approach is problematic because it suggests that the value varies between instances of the type when in fact it does not. A <code class="language-plaintext highlighter-rouge">static</code> property better represents the real semantics of the situation.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">LoginViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">headerSpace</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">24</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This can then be referenced by qualifying the identifier with the classname or <code class="language-plaintext highlighter-rouge">Self</code>, e.g. <code class="language-plaintext highlighter-rouge">LoginViewController.headerSpace</code> or <code class="language-plaintext highlighter-rouge">Self.headerSpace</code>.</p>
<p>A nice alternative is to embed the property within a case-less enum, as follows:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">LoginViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
<span class="kd">private</span> <span class="kd">enum</span> <span class="kt">LayoutMetric</span> <span class="p">{</span>
<span class="kd">static</span> <span class="k">let</span> <span class="nv">headerSpace</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">24</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Then the property can be referenced internally as <code class="language-plaintext highlighter-rouge">LayoutMetric.headerSpace</code>. With this convention we can group semantically related constants and the qualified name tells us what it relates to.</p>
<p>Using an enum in this way is similar to the <code class="language-plaintext highlighter-rouge">namespace</code> concept in C++ which provides a first class way of collecting together type names.</p>
<p>In Swift an enum with no cases is known as an <strong>uninhabited</strong> type. It is a type that cannot be instantiated. As a result there’s no way a sleep deprived developer could start using LayoutMetric variables by mistake.</p>Robin CharltonSyntactic SugarThree Kinds of Nothing2018-08-05T10:00:00+01:002018-08-05T10:00:00+01:00https://rcharlton.github.io/blog/post-three-kinds-of-nothing<p>A gem from Advanced Swift <a href="https://www.objc.io/books/advanced-swift/">objc.io</a> (credited to David Smith on <a href="https://twitter.com/Catfish_Man/status/825080948555292672">Twitter</a>) is the appreciation of Swift’s three representations of nothing. Although the language’s syntactic shortcuts give us succinct code they sometimes mask the details and in doing so blunt our understanding of how things work.</p>
<p>This post explores and contrasts the meaning of the three kinds of nothing: nil, Void and Never.</p>
<h2 id="nil--the-absence-of-a-thing">nil — The absence of a thing</h2>
<p>Everyone is familiar with the value of <code class="language-plaintext highlighter-rouge">nil</code>, or more specifically <code class="language-plaintext highlighter-rouge">Optional.none</code>. It’s the complement to <code class="language-plaintext highlighter-rouge">Optional.some</code> and the semantic analogue of C languages’ <code class="language-plaintext highlighter-rouge">NULL</code>.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">aThing</span><span class="p">:</span> <span class="kt">Optional</span><span class="o"><</span><span class="kt">Thing</span><span class="o">></span> <span class="o">=</span> <span class="o">.</span><span class="k">none</span>
</code></pre></div></div>
<h2 id="void---the-presence-of-nothing">Void — The Presence of nothing</h2>
<p>The Standard Library describes the type <code class="language-plaintext highlighter-rouge">Void</code> as <em>“the return type of functions that don’t explicitly specify a return type”</em>. So we might deduce that functions and methods either return <em>something</em> or they return the <em>presence of nothing</em>:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="kt">String</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Shall we play a game?"</span><span class="p">)</span>
<span class="k">return</span> <span class="s">"something"</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Shall we play a game?"</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">Void</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Or equivalently:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Shall we play a game?"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Since <code class="language-plaintext highlighter-rouge">Void</code> is a type we can instantiate an instance of it.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">nothing</span><span class="p">:</span> <span class="kt">Void</span> <span class="o">=</span> <span class="nf">f</span><span class="p">()</span>
</code></pre></div></div>
<p>And we can also test its equality to show that all kinds of nothing are the same!</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">assert</span><span class="p">(</span><span class="n">nothing</span> <span class="o">==</span> <span class="kt">Void</span><span class="p">())</span>
</code></pre></div></div>
<p>The Standard Library actually defines <code class="language-plaintext highlighter-rouge">Void</code> as equivalent to the empty tuple; giving rise to another equivalent function declaration.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">typealias</span> <span class="kt">Void</span> <span class="o">=</span> <span class="p">()</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="p">()</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Shall we play a game?"</span><span class="p">)</span>
<span class="nf">return</span> <span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>But now things are strange. The empty tuple expression <code class="language-plaintext highlighter-rouge">()</code> is both the type and the value of <em>nothing</em>.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">nothing</span><span class="p">:</span> <span class="p">()</span> <span class="o">=</span> <span class="p">()</span>
</code></pre></div></div>
<h2 id="never--the-thing-which-cannot-be">Never — The thing which cannot be</h2>
<p>Never is a type that has no valid values and cannot be instantiated.</p>
<p>Consider this enum type with no cases.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">enum</span> <span class="kt">Never</span> <span class="p">{</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Such types are termed <em>uninhabited</em>. The set of all possible instances of <code class="language-plaintext highlighter-rouge">Never</code> is empty.</p>
<p>This leads us to a situation where we can declare a variable of the type but have no values with which to initialise it.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">never</span><span class="p">:</span> <span class="kt">Never</span> <span class="o">=</span> <span class="p">???</span>
</code></pre></div></div>
<p>OK, perhaps we can declare a function that returns type <code class="language-plaintext highlighter-rouge">Never</code> and assign that?</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Never</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Shall we play a game?"</span><span class="p">)</span>
<span class="c1">// return ???</span>
<span class="p">}</span>
</code></pre></div></div>
<p>But how can a function return a value of a type that cannot be instantiated?</p>
<p>Swift gives the compilation error <code class="language-plaintext highlighter-rouge">Function with uninhabited return type 'Never' is missing call to another never-returning function on all paths</code> for this declaration. To silence the error the function must call another function that returns <code class="language-plaintext highlighter-rouge">Never</code> like e.g. <code class="language-plaintext highlighter-rouge">fatalError</code>.</p>
<p><code class="language-plaintext highlighter-rouge">Never</code> tells the compiler that the function will <strong>not return</strong>. This contradicts the previous idea that <em>“functions and methods either return something or nothing”</em>. In fact, sometimes they don’t return at all!</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Never</span> <span class="p">{</span>
<span class="nf">fatalError</span><span class="p">(</span><span class="s">"The only winning move is not to play"</span><span class="p">)</span>
<span class="c1">// Execution does not continue.</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And now we can compile code that initialises a value of type <code class="language-plaintext highlighter-rouge">Never</code>.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">never</span><span class="p">:</span> <span class="kt">Never</span> <span class="o">=</span> <span class="nf">f</span><span class="p">()</span>
</code></pre></div></div>
<p>Of course this initialisation is guaranteed to <strong>never</strong> run.</p>Robin Charltonnil, Void & Never