<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://sintraworks.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://sintraworks.github.io/" rel="alternate" type="text/html" /><updated>2025-10-11T09:19:07+00:00</updated><id>https://sintraworks.github.io/feed.xml</id><title type="html">SintraWorks Blog</title><subtitle>Some chatter, babble and prattle about things I find interesting.</subtitle><entry><title type="html">Money Type</title><link href="https://sintraworks.github.io/swift/2023/09/13/money.html" rel="alternate" type="text/html" title="Money Type" /><published>2023-09-13T00:00:00+00:00</published><updated>2023-09-13T00:00:00+00:00</updated><id>https://sintraworks.github.io/swift/2023/09/13/money</id><content type="html" xml:base="https://sintraworks.github.io/swift/2023/09/13/money.html"><![CDATA[<p>In various places where I’ve worked, even various financially-oriented institutions, I’ve found that the type used for dealing with monetary amounts was some form of floating point number. This is never a good idea. Floating point numbers are a bad tool for dealing with decimal values, due to the way they store fractional numbers, and the imprecision that results from that. Especially when those number are added together, substracted from each other, multiplied or divided, which will almost always compound the imprecision and result in increasingly significant rounding errors. The minimum needed to safely deal with monetary amounts is a decimal type: a type that can represent decimals without loss of precision. In Swift we have the <code class="language-plaintext highlighter-rouge">Decimal</code> type to satisfy that need. But the <code class="language-plaintext highlighter-rouge">Decimal</code> type in and of itself is not really enough when dealing with monetary amounts, unless, maybe, when your needs are most basic. When dealing with monetary amounts we are really always dealing with an amount in a particular currency. It is convenient therefore to have a type that can represent an amount in a particular currency (whichever currency that may be), and to be able to perform arithmatic with those amounts, in a type-safe manner. By type-safe, I also mean that you cannot, say, add two amounts representing different currencies together. While it makes sense to add 2 euros to 4 euros, the result being 6 euros, it doesn’t make sense to add 2 euros to 4 dollars. What would that even mean? Unless you have built in provisions for currency exchange. But even then, you’d first be converting one amount to a (more or less) equivalent amount in the other currency, before operating on the amounts.</p>

<p>The Money package aims to offer a convenient abstraction for dealing with monetary amounts. It provides a type-safe environment, where you can represent amounts of any currency, and perform arthmetic operations between amounts of the same currency.</p>

<h3 id="goals">Goals</h3>

<ol>
  <li><strong>Precision</strong>: Allow handling of monetary amounts without loss of precision. This is achieved by using the <code class="language-plaintext highlighter-rouge">Decimal</code> type to represent amounts. Decimals do not suffer from the (potentially) imprecise representations that make floating point numbers inappropriate as a carrier for monetary amounts.</li>
  <li><strong>Type safety</strong>: Disallow arithmetical operations between monetary amounts of different currencies. This is achieved by making the concrete money type (<code class="language-plaintext highlighter-rouge">Tender</code>) generic over its currency, enabling compiler level support when dealing with money types.</li>
  <li>Yet we must be flexible in allowing dynamic creation of concrete monetary amounts, and allowing collections that contain monetary amounts of varying currencies. This is achieved through the <code class="language-plaintext highlighter-rouge">Money</code> protocol.</li>
  <li><strong>Convenience</strong>: Working with monetary amounts should be as easy as working with built-in number types. This is achieved by adding provisions that facilitate day-to-day needs when dealing with monetary amounts.</li>
</ol>

<h3 id="implementation">Implementation</h3>

<p>Currencies are represented by caseless enums that conform to the Currency protocol. We use caseless enums since we want to use currencies as types only. We don’t want instances of currencies, since we don’t need them, and caseless enums cannot be instantiated. Currencies have a <code class="language-plaintext highlighter-rouge">code</code> and a <code class="language-plaintext highlighter-rouge">minorUnitScale</code>. The <code class="language-plaintext highlighter-rouge">minorUnitScale</code> represents the subdivisions of the main (major) currency unit. E.g: Euros and US Dollars are divided into cents, hence they have a <code class="language-plaintext highlighter-rouge">minorUnitScale</code> of <code class="language-plaintext highlighter-rouge">2</code>. Each currency also has a name. We derive the (localized) name from the platform, since Apple platforms provide this information.</p>

<p>We provide default implementations for all properties defined in the protocol to avoid code duplication. The only implementation that is expected to occasionally be overridden is that of the <code class="language-plaintext highlighter-rouge">minorUnitScale</code>. The vast majority of currencies have a unit scale of 2, but some currencies deviate from that. We provide a default list of known currencies, which, we expect, will seldom need to be updated. The list we provide is based on the common currencies known to the Apple platforms, and therefore is not guaranteed to be quite 100% equal to the ISO 4217 list of currencies, but the discrepancies, at the time of this writing are small enough to be negligeable to most. Yet, your needs may vary, so you may choose to alter the list to your needs.</p>

<p>For the represention of monetary amounts we use two related types: <code class="language-plaintext highlighter-rouge">Money</code>, a protocol that represents monetary amounts, and <code class="language-plaintext highlighter-rouge">Tender</code> a type that conforms to <code class="language-plaintext highlighter-rouge">Money</code>. <code class="language-plaintext highlighter-rouge">Tender</code> is generic over the currency it represents, which provides compiler assisted type safety (e.g. disallowing the addition of <code class="language-plaintext highlighter-rouge">Tender</code>s of different currencies), while its conformance to the <code class="language-plaintext highlighter-rouge">Money</code> protocol provides the needed flexibility to work with collections of money of various currencies, and run-time instantiation of monetary amounts whose currency is not known at compile-time.</p>

<p>Think of <code class="language-plaintext highlighter-rouge">Money</code> as the abstract type, providing dynamic flexibility at runtime, and of <code class="language-plaintext highlighter-rouge">Tender</code> as the concrete monetary amount that is tendered (offered) for processing.</p>

<p>While you cannot add or subtract <code class="language-plaintext highlighter-rouge">Tender</code>s of different currencies (which would make no sense without first converting them to the same currency), or multiply one <code class="language-plaintext highlighter-rouge">Tender</code> by another, you <em>can</em> perform arithmetic on <code class="language-plaintext highlighter-rouge">Tender</code>s. E.g. you can multiply a <code class="language-plaintext highlighter-rouge">Tender</code> by a numeric type: €5.42 * 3,14.</p>

<h3 id="usage">Usage</h3>

<p>Currencies are already provided, so you should not need to create currencies. However, if, for whatever reason, you find you need to create custom currencies, you can create new currency types as enums that conform to the <code class="language-plaintext highlighter-rouge">Currency</code> protocol. If your custom unit needs a <code class="language-plaintext highlighter-rouge">minorUnitScale</code> that is not the standard <code class="language-plaintext highlighter-rouge">2</code>, then you need to provide a custom implementation in the body of the enum:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>public enum MyCustomCurrency: Currency { public static var minorUnitScale: Int { 4 } }
</code></pre></div></div>

<p>Otherwise, you can just create an enum with an empty body:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>public enum MyCustomCurrency: Currency {}
</code></pre></div></div>

<p>Create concrete monetary amounts by creating instances of <code class="language-plaintext highlighter-rouge">Tender</code>, providing the currency it is generic over. Preferably create <code class="language-plaintext highlighter-rouge">Tender</code>s from <code class="language-plaintext highlighter-rouge">Decimal</code>s, <code class="language-plaintext highlighter-rouge">Int</code>s or <code class="language-plaintext highlighter-rouge">String</code>s, rather than from floating point values, although you can use those if you really need to.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    let amount: Decimal = 3.14 // (or, preferably: Decimal(string: "3.14"))
    let someMoney1 = Tender&lt;EUR&gt;(amount)
    let someMoney2 = Tender&lt;USD&gt; = 5
    let someMoney3 = Tender&lt;JPY&gt; = 3000
    let someMoney4 = Tender&lt;SEK&gt;("42")
    let someMoney5 = Tender&lt;USD&gt;("5,501", locale: Locale(identifier: "nl_NL")
    let someMoney6 = Tender&lt;USD&gt;("5.501", locale: Locale(identifier: "en_US")

    let ratherNot = Tender&lt;EUR&gt;(3.02) // Floating point number are approximations, they cannot be trusted to produce correct results.
</code></pre></div></div>

<p>You cannot add or subtract <code class="language-plaintext highlighter-rouge">Tender</code>s of different currencies. You can however add and subtract <code class="language-plaintext highlighter-rouge">Tender</code>s of the same currency:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    let sum = someMoney5 + someMoney6
</code></pre></div></div>

<p>You can also compare <code class="language-plaintext highlighter-rouge">Tender</code>s of the same currency:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    if someMoney5 &lt; someMoney6 {
        …do something…
    }
</code></pre></div></div>

<p>Take a look at the TenderTests and TenderAlgebraTests files (in the repo) for a good overview of what is possible.</p>

<p>Of course, we will usually need to display our monetary amounts to our users. To this end, the <code class="language-plaintext highlighter-rouge">Money</code> type provides a <code class="language-plaintext highlighter-rouge">displayable</code>, of type <code class="language-plaintext highlighter-rouge">Displayable</code>, that facilitates the translation from the amount to user friendly strings. When you want to diplay an amount to the user, ask the instance for a <code class="language-plaintext highlighter-rouge">displayable</code>, and ask the <code class="language-plaintext highlighter-rouge">Displayable</code> for the desired string:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        someMoney5.displayable.formatted
        someMoney5.displayable.formattedTruncatingDecimals()
        someMoney5.displayable.formattedTruncatingDecimals(for: Locale(identifier: "en_GB")
        someMoney5.displayable.formattedWithoutCurrencySymbol()
        someMoney5.displayable.formattedWithoutCurrencySymbol(for: Locale(identifier: "pt_PT")
</code></pre></div></div>

<p>You can also ask a displayable for the currency symbol and decimal separator and currency name.</p>

<p>If you need to store collections of amounts with various currencies, you cannot create a collection of <code class="language-plaintext highlighter-rouge">Tender</code>, since <code class="language-plaintext highlighter-rouge">Tender</code> is generic over its currency. In this case you should create a collection of <code class="language-plaintext highlighter-rouge">Money</code>, since Money, while holding a currency, is not generic over it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        var variousCurrencies = [any Money]()
        variousCurrencies.append(Tender&lt;EUR&gt;(1))
        variousCurrencies.append(Tender&lt;USD&gt;(0.5))
</code></pre></div></div>

<p>Additionally, if you need to create monetary amounts at runtime, whose currency you do not know at compile time, use the <code class="language-plaintext highlighter-rouge">Money</code> type to model your data. Use the <code class="language-plaintext highlighter-rouge">MoneyFactory</code> to obtain a concrete <code class="language-plaintext highlighter-rouge">Tender</code> (typed as <code class="language-plaintext highlighter-rouge">Money</code>) from an amount and currency code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        let myRuntTimeAmount = Decimal(42)
        let myRuntimeCurrency = SupportedCurrency.INR
        
        try MoneyFactory.moneyFrom(amount: myRuntTimeAmount, currency: myRuntimeCurrency)
</code></pre></div></div>

<p>You can find the full source code <a href="https://github.com/SintraWorks/Money">here</a>.</p>]]></content><author><name></name></author><category term="swift" /><category term="swift" /><summary type="html"><![CDATA[Using a Money Type for Typesafe Currency Handling.]]></summary></entry><entry><title type="html">Fractions Revisited</title><link href="https://sintraworks.github.io/swift/2023/09/12/fractions-revisited.html" rel="alternate" type="text/html" title="Fractions Revisited" /><published>2023-09-12T00:00:00+00:00</published><updated>2023-09-12T00:00:00+00:00</updated><id>https://sintraworks.github.io/swift/2023/09/12/fractions-revisited</id><content type="html" xml:base="https://sintraworks.github.io/swift/2023/09/12/fractions-revisited.html"><![CDATA[<p>This post is a follow-up on myprevious  <a href="/fractions/math/swift/2020/03/18/fractions.html">post about fractions</a> from some years ago, where I discuss the creation of the Fraction type. (I meant to publish this much sooner, but life got in the way.)</p>

<p>As I started working more with Fraction, I found that I needed to be able to more easily mix Fractions with the base numeric types during calculations, as well as making it easy to instantiate fractions from numeric literals. I also found it inconvenient to only have failable initializers, since often I can guarantee I will only pass in valid values to the initializers. So I added non-failable alternatives that are guaranteed to instantiate (or, crash if you pass in invalid values).</p>

<p>This post picks up from where the previous post on Fractions left of, so I advise you to read that first, if you haven’t already, and then come back here.</p>

<h3 id="guaranteed-initializers">Guaranteed Initializers</h3>

<p>If you know what you’re doing, i.e. you have taken care to eliminate the possibility of providing invalid input, you can now use guaranteed initializers when instantiating Fractions. This eliminates the frequent need for unwrapping and/or forced-unwrapping when creating fractions.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">verifiedNumerator</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">precondition</span><span class="p">(</span><span class="n">verifiedNumerator</span> <span class="o">&gt;</span> <span class="kt">Int</span><span class="o">.</span><span class="n">min</span><span class="p">,</span> <span class="s">"Illegal numerator value: Int.min is not allowed"</span><span class="p">)</span>

    <span class="k">self</span><span class="o">.</span><span class="n">numerator</span> <span class="o">=</span> <span class="n">verifiedNumerator</span>
    <span class="k">self</span><span class="o">.</span><span class="n">denominator</span> <span class="o">=</span> <span class="mi">1</span>
<span class="p">}</span>

<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">verifiedNumerator</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">verifiedDenominator</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">wholes</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">precondition</span><span class="p">(</span><span class="n">verifiedNumerator</span> <span class="o">&gt;</span> <span class="kt">Int</span><span class="o">.</span><span class="n">min</span><span class="p">,</span> <span class="s">"Illegal numerator value: Int.min is not allowed"</span><span class="p">)</span>
    <span class="nf">precondition</span><span class="p">(</span><span class="n">verifiedDenominator</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"0 is an illegal value for the denominator"</span><span class="p">)</span>
    
    <span class="k">self</span><span class="o">.</span><span class="n">numerator</span> <span class="o">=</span> <span class="n">verifiedNumerator</span> <span class="o">+</span> <span class="p">(</span><span class="n">verifiedDenominator</span> <span class="o">*</span> <span class="n">wholes</span><span class="p">)</span>
    <span class="k">self</span><span class="o">.</span><span class="n">denominator</span> <span class="o">=</span> <span class="n">verifiedDenominator</span>
<span class="p">}</span>

</code></pre></div></div>

<p>The preconditions ensure you will crash early during development if you pass in an illegal numerator value. Thus reducing the chance of programmer error in production code. In the second initializer I have also added the option to pass in an amount of whole numbers, as a convenience, if you want to turn e.g. 2•2/3 into a Fraction. This <code class="language-plaintext highlighter-rouge">wholes</code> option was also added to the existing initializers.</p>

<h3 id="instantiating-fractions-from-literals">Instantiating Fractions from Literals</h3>

<p>While the Fraction type already had initializers, I regularly found it inconvenient to have to instantiate fractions through those wordy initializers:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">f</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<p>Therefore <code class="language-plaintext highlighter-rouge">Fraction</code> now conforms to <code class="language-plaintext highlighter-rouge">ExpressibleByIntegerLiteral</code> and <code class="language-plaintext highlighter-rouge">ExpressibleByFloatLiteral</code>, allowing the creation of Fraction instances from <code class="language-plaintext highlighter-rouge">Int</code>s and the various float types. So you can now write things like:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">wholeFraction</span><span class="p">:</span> <span class="kt">Fraction</span> <span class="o">=</span> <span class="mi">3</span>
<span class="k">let</span> <span class="nv">wholeFraction2</span> <span class="o">=</span> <span class="n">wholeFraction</span> <span class="o">*</span> <span class="mi">2</span>

<span class="k">let</span> <span class="nv">fractionalFraction</span><span class="p">:</span> <span class="kt">Fraction</span> <span class="o">=</span> <span class="mf">3.9</span>
<span class="k">let</span> <span class="nv">fractionalFraction2</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="n">fractionalFraction</span> <span class="o">/</span> <span class="mf">3.3</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Fraction</code> now sports an adjustable static var to guide the precision when creating fractions from floating point values, which defaults to a precision of four digits:
<code class="language-plaintext highlighter-rouge">static var significantFloatingPointDigits = 4</code>. You may adjust this value as needed.</p>

<p>The test suite has been accordingly updated.</p>

<p>You can find the full source code <a href="https://github.com/SintraWorks/Fraction/">here</a>.</p>]]></content><author><name></name></author><category term="swift" /><category term="swift" /><summary type="html"><![CDATA[Mixing fractions with other numeric types.]]></summary></entry><entry><title type="html">SwiftUI: Showing a control and implementing avoidance</title><link href="https://sintraworks.github.io/swift/swiftui/2022/08/22/bottom-wheel-picker.html" rel="alternate" type="text/html" title="SwiftUI: Showing a control and implementing avoidance" /><published>2022-08-22T00:00:00+00:00</published><updated>2022-08-22T00:00:00+00:00</updated><id>https://sintraworks.github.io/swift/swiftui/2022/08/22/bottom-wheel-picker</id><content type="html" xml:base="https://sintraworks.github.io/swift/swiftui/2022/08/22/bottom-wheel-picker.html"><![CDATA[<p>In this post we will create a SwiftUI control that can be popped up from the bottom of the screen. We will also make it possible to allow a specific view (usually the view that triggers the control and reflects the chosen value) to be raised, if it would otherwise be obscured by the control when the control pops up. This post assumes you have a reasonable knowledge of SwiftUI and are at least somewhat familiar with more advanced topics such as bindings, geometery readers, preference keys, etc. It won’t go into details as to how they work. I will simply show how to use them to achieve the desired effact.</p>

<p>We’ll use a wheel picker to demonstrate the technique, but of course it could be used for other content too.</p>

<h3 id="wheel-picker">Wheel Picker</h3>

<p>To start with, let’s create a view that shows a wheel picker and a dismissal button, formatted to look somewhat like a system control.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">WheelPicker</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">selection</span><span class="p">:</span> <span class="kt">String</span>
    <span class="kd">@Binding</span> <span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">show</span><span class="p">:</span> <span class="kt">Bool</span>
    <span class="k">var</span> <span class="nv">values</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>
    <span class="k">var</span> <span class="nv">dismissButtonTitle</span><span class="p">:</span> <span class="kt">String</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span> <span class="p">{</span>
            <span class="kt">VStack</span> <span class="p">{</span>
                <span class="kt">Divider</span><span class="p">()</span>
                <span class="kt">Spacer</span><span class="p">()</span>
                <span class="kt">Button</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="p">{</span>
                    <span class="n">withAnimation</span> <span class="p">{</span>
                        <span class="k">self</span><span class="o">.</span><span class="n">show</span> <span class="o">=</span> <span class="kc">false</span>
                    <span class="p">}</span>
                <span class="p">})</span> <span class="p">{</span>
                    <span class="kt">HStack</span> <span class="p">{</span>
                        <span class="kt">Spacer</span><span class="p">()</span>
                        <span class="kt">Text</span><span class="p">(</span><span class="n">dismissButtonTitle</span><span class="p">)</span>
                            <span class="o">.</span><span class="nf">padding</span><span class="p">(</span><span class="o">.</span><span class="n">horizontal</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
                    <span class="p">}</span>
                <span class="p">}</span>
                <span class="kt">Divider</span><span class="p">()</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">background</span><span class="p">(</span><span class="kt">Color</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">uiColor</span><span class="p">:</span> <span class="o">.</span><span class="n">secondarySystemGroupedBackground</span><span class="o">.</span><span class="nf">withAlphaComponent</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)))</span>
            <span class="kt">Picker</span><span class="p">(</span><span class="nv">selection</span><span class="p">:</span> <span class="err">$</span><span class="n">selection</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">Text</span><span class="p">(</span><span class="s">""</span><span class="p">))</span> <span class="p">{</span>
                <span class="kt">ForEach</span><span class="p">(</span><span class="n">values</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
                    <span class="kt">Text</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="nv">$0</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">pickerStyle</span><span class="p">(</span><span class="o">.</span><span class="n">wheel</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The picker needs some values to show. To keep things simple, we only allow strings. We also allow setting the title of the button that dismisses the view when tapped. Finally, there are two bindings so that we can communicate with the client about when to hide the control, and about the selection. The <code class="language-plaintext highlighter-rouge">show</code> binding is readonly, since we only use it to communicate back up the chain that the dismissal button was pressed. The <code class="language-plaintext highlighter-rouge">selection</code> binding is two way: we simply pass it on to the picker. This should result in an initial selection, and when the user changes the selection the new value will be passed back up the chain. We use the <code class="language-plaintext highlighter-rouge">withAnimation</code> modifier to ensure the shifts are nicely animated.</p>

<h3 id="bottom-wheel-picker">Bottom Wheel Picker</h3>
<p>Next, we need to implement a mechanism that can show the picker at the bottom of the screen when it needs to be visible, and that hides it just below the screen when it needs to be hidden.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">BottomWheelPicker</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="c1">/// A binding that controls showing or hiding the picker</span>
    <span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">show</span> <span class="p">:</span> <span class="kt">Bool</span>
    <span class="c1">/// A binding that manages the selected option from the collection of values</span>
    <span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">selection</span><span class="p">:</span> <span class="kt">String</span>
    <span class="c1">/// The collection of values available to the picker</span>
    <span class="k">var</span> <span class="nv">values</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">WheelPicker</span><span class="p">(</span><span class="nv">selection</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="err">$</span><span class="n">selection</span><span class="p">,</span>
                    <span class="nv">show</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="err">$</span><span class="n">show</span><span class="p">,</span>
                    <span class="nv">values</span><span class="p">:</span> <span class="n">values</span><span class="p">,</span>
                    <span class="nv">dismissButtonTitle</span><span class="p">:</span> <span class="s">"Done"</span><span class="p">)</span>
        <span class="o">.</span><span class="nf">fixedSize</span><span class="p">()</span> <span class="c1">// Ensures the WheelPicker is exactly as large as it needs to be, and no larger</span>
        <span class="o">.</span><span class="nf">background</span><span class="p">(</span>
            <span class="kt">Color</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">uiColor</span><span class="p">:</span> <span class="o">.</span><span class="n">systemGray6</span><span class="p">)</span>
        <span class="p">)</span>
        <span class="c1">// When not shown, hide us below the bottom of the screen.</span>
        <span class="o">.</span><span class="nf">offset</span><span class="p">(</span><span class="nv">y</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">show</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="kt">UIScreen</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">height</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">show</code>, <code class="language-plaintext highlighter-rouge">selection</code> and <code class="language-plaintext highlighter-rouge">values</code> are simply passed directly to the picker.</p>

<p>In the <code class="language-plaintext highlighter-rouge">body</code> we provide the <code class="language-plaintext highlighter-rouge">WheelPicker</code>, and use <code class="language-plaintext highlighter-rouge">fixedSize</code> to ensure our wheel picker view is only as large as it needs to be. We set a background color in the <code class="language-plaintext highlighter-rouge">background</code> modifier, and use the <code class="language-plaintext highlighter-rouge">offset</code> modifier so that the picker is hidden below the screen when not shown.</p>

<h3 id="showing-and-hiding-the-picker-in-a-container-view">Showing and Hiding the Picker in a Container View</h3>

<p>Now we need a view that makes use of the wheel picker to allow the selection of a value from a list of predefined values.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="k">var</span> <span class="nv">showPicker</span> <span class="o">=</span> <span class="kc">false</span>
    <span class="kd">@State</span> <span class="k">var</span> <span class="nv">selection</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"€1.500"</span>
    <span class="kd">@State</span> <span class="k">var</span> <span class="nv">pickerHeight</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">var</span> <span class="nv">values</span> <span class="o">=</span> <span class="p">[</span><span class="s">"€1.500"</span><span class="p">,</span> <span class="s">"€3.000"</span><span class="p">,</span> <span class="s">"€6.000"</span><span class="p">,</span> <span class="s">"€9.000"</span><span class="p">,</span> <span class="s">"€10.000"</span><span class="p">,</span> <span class="s">"€11.000"</span><span class="p">,</span> <span class="s">"€12.000"</span><span class="p">,</span> <span class="s">"€13.000"</span><span class="p">,</span> <span class="s">"€14.000"</span><span class="p">,</span> <span class="s">"€15.000"</span><span class="p">,</span> <span class="s">"€16.000"</span><span class="p">,</span> <span class="s">"€17.000"</span><span class="p">,</span> <span class="s">"€18.000"</span><span class="p">]</span>
    <span class="kd">@State</span> <span class="k">var</span> <span class="nv">showMoreText</span> <span class="o">=</span> <span class="kc">true</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span> <span class="p">{</span>
            <span class="kt">Group</span> <span class="p">{</span>
                <span class="kt">Spacer</span><span class="p">()</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"Let's pick an amount"</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
                <span class="kt">Button</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="p">{</span>
                    <span class="n">withAnimation</span> <span class="p">{</span>
                        <span class="k">self</span><span class="o">.</span><span class="n">showPicker</span><span class="o">.</span><span class="nf">toggle</span><span class="p">()</span>
                    <span class="p">}</span>
                <span class="p">})</span> <span class="p">{</span>
                    <span class="kt">Text</span><span class="p">(</span><span class="n">selection</span><span class="p">)</span>
                        <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="kt">Group</span> <span class="p">{</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"The chosen amount"</span><span class="p">)</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"is…"</span><span class="p">)</span>
                <span class="kt">Text</span><span class="p">(</span><span class="n">selection</span><span class="p">)</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"Be kind"</span><span class="p">)</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"…and friendly,"</span><span class="p">)</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"really."</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="n">showMoreText</span> <span class="p">{</span>
                <span class="kt">Spacer</span><span class="p">()</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"It works!"</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">minWidth</span><span class="p">:</span> <span class="mi">300</span><span class="p">,</span> <span class="nv">minHeight</span><span class="p">:</span> <span class="mi">600</span><span class="p">)</span>
        <span class="o">.</span><span class="nf">overlay</span><span class="p">(</span><span class="kt">BottomWheelPicker</span><span class="p">(</span><span class="nv">show</span><span class="p">:</span> <span class="err">$</span><span class="n">showPicker</span><span class="p">,</span> <span class="nv">selection</span><span class="p">:</span> <span class="err">$</span><span class="n">selection</span><span class="p">,</span> <span class="nv">values</span><span class="p">:</span> <span class="n">values</span><span class="p">),</span> <span class="nv">alignment</span><span class="p">:</span> <span class="o">.</span><span class="n">bottom</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The code above creates a view that has a button that reflects a monetary amount. The button acts as a toggle: Tapping it will show the picker, if it is hidden, and hide the picker, if it is on-screen.</p>

<p>The <code class="language-plaintext highlighter-rouge">showPicker</code> state variable is used to control the visibility of the picker. It is tied to the button, naturally, so that we trigger view updates when the button is tapped.</p>

<p>The <code class="language-plaintext highlighter-rouge">selection</code> state variable is tied to the button title. It will be updated when the user selects a value from the picker.</p>

<p>Below we have some filler text, mostly for demonstration purposes.</p>

<p>We use the <code class="language-plaintext highlighter-rouge">frame</code> modifier so that we have a reasonably sized view in a playground. If the content is hosted in a real app, you should remove this modifier.</p>

<p>Finally, but rather importantly, we use the <code class="language-plaintext highlighter-rouge">overlay</code> modifier to host the <code class="language-plaintext highlighter-rouge">BottomWheelPicker</code>, passing along the relevant state variables and picker values. When the picker is triggered it will be animated in, on top of the content view.</p>

<p>We now have all the pieces in place to show and hide the wheel picker on command. You could use the above code in a playground to see it in action.</p>

<p>There is a variable <code class="language-plaintext highlighter-rouge">showMoreText</code>, that defaults to true. It ensures the button is quite high up in the content view, so that it is not obscured by the picker, when the picker is shown. If you set that variable to <code class="language-plaintext highlighter-rouge">false</code> before running the playground, you’ll see that the button is considerably lower on the screen, and that it gets obscured by the picker when the picker is visible.</p>

<p>We need to provide a mechanism that allows us to raise the button when showing the picker, but only if the button would be obscured by the picker:</p>

<h3 id="wheel-picker-avoidance">Wheel Picker Avoidance</h3>

<p>To enable view avoidance, we need to do a fair bit of bookkeeping. We need to know the height of the picker, and we need to know the y-coordinate of the bottom of the view that wants to avoid the picker (in our case, the button). Then we can use that data to determine if, and if so, how much, the avoiding view needs to shift up, not to be obscured by the picker.</p>

<p>We also need a way to be notified when the picker is shown, and when it is hidden so that we can shift the view in the appropriate direction when we receive such a notification.</p>

<p>Let’s start by enabling access to the picker’s frame.</p>

<p>We create a preference key to hold the frame information. It is quite basic, as it doesn’t need to accumulate any values.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">FramePreferenceKey</span><span class="p">:</span> <span class="kt">PreferenceKey</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">defaultValue</span><span class="p">:</span> <span class="kt">CGRect</span> <span class="o">=</span> <span class="o">.</span><span class="n">zero</span>
    <span class="kd">static</span> <span class="kd">func</span> <span class="nf">reduce</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="k">inout</span> <span class="kt">CGRect</span><span class="p">,</span> <span class="nv">nextValue</span><span class="p">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">CGRect</span><span class="p">)</span> <span class="p">{}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then we add a geometry reader to the background modifier, to provide the frame of the wheel picker. We put the reader around the color, and then provide the preference key hanging off the color with the frame of the picker in global coordinates.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="o">.</span><span class="nf">background</span><span class="p">(</span>
            <span class="c1">// Use the geometry reader to extract the frame of the WheelPicker</span>
            <span class="kt">GeometryReader</span> <span class="p">{</span> <span class="n">geometry</span> <span class="k">in</span>
                <span class="kt">Color</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">uiColor</span><span class="p">:</span> <span class="o">.</span><span class="n">systemGray6</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">preference</span><span class="p">(</span><span class="nv">key</span><span class="p">:</span> <span class="kt">FramePreferenceKey</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="n">geometry</span><span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">.</span><span class="n">global</span><span class="p">))</span>
        <span class="p">})</span>
</code></pre></div></div>

<p>We also add notifications to be sent when showing or hiding the picker.</p>

<p>At the top of the <code class="language-plaintext highlighter-rouge">BottomWheelPicker</code>:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">BottomWheelPicker</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">let</span> <span class="nv">willShowNotification</span><span class="p">:</span> <span class="kt">NSNotification</span><span class="o">.</span><span class="kt">Name</span> <span class="o">=</span> <span class="kt">NSNotification</span><span class="o">.</span><span class="kt">Name</span><span class="p">(</span><span class="nv">rawValue</span><span class="p">:</span> <span class="s">"PickerWillShow"</span><span class="p">)</span>
    <span class="kd">static</span> <span class="k">let</span> <span class="nv">willHideNotification</span><span class="p">:</span> <span class="kt">NSNotification</span><span class="o">.</span><span class="kt">Name</span> <span class="o">=</span> <span class="kt">NSNotification</span><span class="o">.</span><span class="kt">Name</span><span class="p">(</span><span class="nv">rawValue</span><span class="p">:</span> <span class="s">"PickerWillHide"</span><span class="p">)</span>
</code></pre></div></div>

<p>Between the <code class="language-plaintext highlighter-rouge">background</code> and <code class="language-plaintext highlighter-rouge">offset</code> modifiers add this modifier:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="nf">onPreferenceChange</span><span class="p">(</span><span class="kt">FramePreferenceKey</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">newFrame</span> <span class="k">in</span>
<span class="c1">// Post a notification that we are showing or hiding, passing along the resulting frame info</span>
<span class="k">let</span> <span class="nv">notificationName</span> <span class="o">=</span> <span class="n">show</span> <span class="p">?</span> <span class="k">Self</span><span class="o">.</span><span class="nv">willShowNotification</span> <span class="p">:</span> <span class="k">Self</span><span class="o">.</span><span class="n">willHideNotification</span>
<span class="kt">NotificationCenter</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">post</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="n">notificationName</span><span class="p">,</span> <span class="nv">object</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span> <span class="nv">userInfo</span><span class="p">:</span> <span class="p">[</span><span class="s">"frame"</span><span class="p">:</span> <span class="n">newFrame</span><span class="p">])</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The following extension to <code class="language-plaintext highlighter-rouge">Notification</code> provides convenient access to <code class="language-plaintext highlighter-rouge">frame</code> info in its user dictionary:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Notification</span> <span class="p">{</span>
    <span class="c1">/// Extends `Notificaton` with a shortcut to acces a frame from its user info dict.</span>
    <span class="k">var</span> <span class="nv">frame</span><span class="p">:</span> <span class="kt">CGRect</span> <span class="p">{</span>
        <span class="n">userInfo</span><span class="p">?[</span><span class="s">"frame"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">CGRect</span> <span class="p">??</span> <span class="o">.</span><span class="n">zero</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We now have access to the frame of the picker, as it changes, and post a notification whenever the picker is shown or hidden. What we still lack is a mechanism to observe these changes and react to them.</p>

<p>To enable any view to avoid the picker, we are going to create a view modifier. This modifier can be attached to any view that needs to avoid the picker. Depending on the buildup of the encompassing view, more than one view might have this view modifier attached, but most likely you are only going to want to attach it to a single view in the scene, since usually all the views above it will also be shifted up (e.g. because you are in a vertical stack view).</p>

<p>The view modifier will provide a customisable padding to allow control over how much space to leave between the picker and the avoiding view when shifting it out of the way.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">BottomWheelPickerAdaptive</span><span class="p">:</span> <span class="kt">ViewModifier</span> <span class="p">{</span>
    <span class="c1">/// The current frame of the picker. This will be either zero (when the picker is hidden), or the actual frame (when the picker is shown)</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">pickerFrame</span><span class="p">:</span> <span class="kt">CGRect</span> <span class="o">=</span> <span class="o">.</span><span class="n">zero</span>
    <span class="c1">/// The offset to be applied to the adaptee's bottom.</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">offset</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="c1">/// The current frame of the adaptee.</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">adapteeFrame</span><span class="p">:</span> <span class="kt">CGRect</span> <span class="o">=</span> <span class="o">.</span><span class="n">zero</span>
    <span class="c1">/// A padding to be apply to the offset to ensure the adaptee does not rest flush on the picker.</span>
    <span class="kd">public</span> <span class="k">var</span> <span class="nv">padding</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">12</span>

    <span class="kd">func</span> <span class="nf">body</span><span class="p">(</span><span class="nv">content</span><span class="p">:</span> <span class="kt">Content</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="n">content</span>
        <span class="c1">// padd the bottom by offset. This will effectively raise the adapting view when offset &gt; 0.</span>
            <span class="o">.</span><span class="nf">padding</span><span class="p">(</span><span class="o">.</span><span class="n">bottom</span><span class="p">,</span> <span class="n">offset</span><span class="p">)</span>
        <span class="c1">// subscribe to wheel picker frame changes</span>
            <span class="o">.</span><span class="nf">onReceive</span><span class="p">(</span><span class="kt">Publishers</span><span class="o">.</span><span class="n">wheelPickerFrame</span><span class="p">)</span> <span class="p">{</span> <span class="n">pickerFrame</span> <span class="k">in</span>
                <span class="n">withAnimation</span> <span class="p">{</span>
                    <span class="k">self</span><span class="o">.</span><span class="n">pickerFrame</span> <span class="o">=</span> <span class="n">pickerFrame</span>

                    <span class="k">guard</span> <span class="n">pickerFrame</span><span class="o">.</span><span class="n">height</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="p">{</span>
                        <span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span>
                        <span class="k">return</span>
                    <span class="p">}</span>

                    <span class="c1">// The padding ensures the picker will not sit too closely below the adaptee, even if it wouldn't obscure any part of the adaptee.</span>
                    <span class="c1">//</span>
                    <span class="c1">// Additionally, only set an offset if the adaptee's bottom (extended by the amount of padding) will be obscured by the picker,</span>
                    <span class="c1">// i.e. only when the offset required for avoidance is greater than zero, because negative values mean the adaptee will remain sufficiently clear from the picker, without raising it.</span>
                    <span class="n">offset</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="p">(</span><span class="n">adapteeFrame</span><span class="o">.</span><span class="n">maxY</span> <span class="o">+</span> <span class="n">padding</span><span class="p">)</span> <span class="o">-</span> <span class="n">pickerFrame</span><span class="o">.</span><span class="n">minY</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">background</span><span class="p">(</span>
                <span class="kt">GeometryReader</span> <span class="p">{</span> <span class="n">geometry</span> <span class="k">in</span>
                    <span class="c1">// Use a neutral view to allow us to obtain the frame of the content (= adaptee)</span>
                    <span class="kt">Color</span><span class="o">.</span><span class="n">clear</span>
                        <span class="o">.</span><span class="nf">preference</span><span class="p">(</span><span class="nv">key</span><span class="p">:</span> <span class="kt">FramePreferenceKey</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="n">geometry</span><span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">.</span><span class="n">global</span><span class="p">))</span>
                <span class="p">}</span>
            <span class="p">)</span>
        <span class="c1">// Here we subscribe to changes to the frame of the adaptee.</span>
            <span class="o">.</span><span class="nf">onPreferenceChange</span><span class="p">(</span><span class="kt">FramePreferenceKey</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">newFrame</span> <span class="k">in</span>
                <span class="n">adapteeFrame</span> <span class="o">=</span> <span class="n">newFrame</span>
            <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The crux of the view modifier, is that it shows its content with a bottom offset applied to it (<code class="language-plaintext highlighter-rouge">.padding(.bottom, offset)</code>). It accesses the content frame info in the background modifier, through a <code class="language-plaintext highlighter-rouge">GeometryReader</code>, and subscribes to frame changes of the adaptee (the avoiding view) in the <code class="language-plaintext highlighter-rouge">onPreferenceChange</code> modifier.</p>

<p>The subscription to changes to the picker wheel frame is established in the <code class="language-plaintext highlighter-rouge">onReceive</code> modifier, and the offset is calculated and set within its closure.</p>

<p>Finally, we want to make access to this view modifier convenient, which is what the following extension on <code class="language-plaintext highlighter-rouge">View</code> does:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="c1">/// Provides bottom wheel picker avoidance behavior</span>
    <span class="kd">func</span> <span class="nf">bottomWheelPickerAdaptive</span><span class="p">(</span><span class="nv">padding</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">12</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">ModifiedContent</span><span class="p">(</span><span class="nv">content</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span> <span class="nv">modifier</span><span class="p">:</span> <span class="kt">BottomWheelPickerAdaptive</span><span class="p">(</span><span class="nv">padding</span><span class="p">:</span> <span class="n">padding</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, all that is left to do, is to add the <code class="language-plaintext highlighter-rouge">bottomWheelPickerAdaptive</code> modifier to the main view. We want the button showing the current amount to always remain visible, so that’s where we add the modifier. The <code class="language-plaintext highlighter-rouge">Group</code> holding the button now becomes:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Group</span> <span class="p">{</span>
                <span class="kt">Spacer</span><span class="p">()</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"Let's pick an amount"</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
                <span class="kt">Button</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="p">{</span>
                    <span class="n">withAnimation</span> <span class="p">{</span>
                        <span class="k">self</span><span class="o">.</span><span class="n">showPicker</span><span class="o">.</span><span class="nf">toggle</span><span class="p">()</span>
                    <span class="p">}</span>
                <span class="p">})</span> <span class="p">{</span>
                    <span class="kt">Text</span><span class="p">(</span><span class="n">selection</span><span class="p">)</span>
                        <span class="o">.</span><span class="nf">bottomWheelPickerAdaptive</span><span class="p">()</span>
                        <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
                <span class="p">}</span>
            <span class="p">}</span>
</code></pre></div></div>

<p>I hope this compact post was clear enough to let you use this technique in any project that might benefit from it.</p>

<p>You can find the full source code, ready for use in an Xcode Playground, in a Github gist <a href="https://gist.github.com/SintraWorks/3059d705803fa1036c159798884eb7e9">here</a>.</p>]]></content><author><name></name></author><category term="swift" /><category term="swiftui" /><category term="swift" /><category term="swiftui" /><summary type="html"><![CDATA[Showing a view containing a control from the bottom, and implementing view avoidance so that the related view is not obscured.]]></summary></entry><entry><title type="html">An Adaptable Segmented Control</title><link href="https://sintraworks.github.io/swift/swiftui/2020/07/07/segmentedpicker.html" rel="alternate" type="text/html" title="An Adaptable Segmented Control" /><published>2020-07-07T00:00:00+00:00</published><updated>2020-07-07T00:00:00+00:00</updated><id>https://sintraworks.github.io/swift/swiftui/2020/07/07/segmentedpicker</id><content type="html" xml:base="https://sintraworks.github.io/swift/swiftui/2020/07/07/segmentedpicker.html"><![CDATA[<p><img src="https://sintraworks.com/media/SegmentedPicker.mp4" alt="SegmentedPicker Demo Clip" /></p>

<p>On iOS, especially on iPads, the dimensions of the size of the views we work with have, over the years, become increasingly flexible and varied. Additionally, accessibility features, like dynamic, user controllable font size have been with us for a while. This means that our user interface items need to be increasingly flexible so that they can adapt to their sometimes dynamically changing environment.</p>

<p>Not so long ago, I found myself faced with a small challenge: I had created a segmented control in SwiftUI, that supports a visual style particular to one of the apps I work on. When I started testing how the control behaved, especially when combining dynamic font size changes with multi-tasking split screens on iPads, I found an issue: Even though my control only had three segments with short titles, at the larger accessibility font sizes, especially if the screen was split due to multitasking mode, the segments would not fit. After thinking about the various ways in which this issue could be solved, I quickly settled on allowing the control to dynamically change to a popup-menu-style control when space gets tight. Now, in UIKit, I knew exactly how to do that. But trying to do this in SwiftUI, which I’m fairly new to, and which uses a completely different approach to building layouts, this was a bit more challenging. Yet, a fun project to dive into.</p>

<p>The solution is made possible through the use of <code class="language-plaintext highlighter-rouge">GeometryReaders</code>, <code class="language-plaintext highlighter-rouge">preferences</code> and <code class="language-plaintext highlighter-rouge">anchors</code>. This post will assume you are already familiar with those, and will simply explain how to arrive at a solution using these and other relevant techniques, like <code class="language-plaintext highlighter-rouge">@Binding</code>s. If you are unfamiliar with <code class="language-plaintext highlighter-rouge">GeometryReaders</code>, <code class="language-plaintext highlighter-rouge">PreferenceKeys</code>, etc, I suggest you read up on those elsewhere. A website that I found particulalry helpful when figuring all this out is <a href="https://swiftui-lab.com">The SwiftUI Lab</a>. Also very informative is an excellent book by objc.io called <a href="https://www.objc.io/books/thinking-in-swiftui/">Thinking in SwiftUI</a>.</p>

<!-- We want this control to play nicely with appearance mode (light and dark) and with accessibility font sizes, we'll cover those aspects too. -->

<h3 id="lets-get-to-it">Let’s get to it…</h3>

<p>We start by creating the basic segmented control. Each segment will consist of a button that performs a very specific action when tapped: it sets the active segment. We visually indicate the active segment by underlining it with a thin yellow beam. We’ll call this control a ‘SegmentedPicker’.</p>

<p>Before we can build the segmented control, however, we need to implement a view type to provide the segments. We’ll call that a <code class="language-plaintext highlighter-rouge">PickerButton</code>, and here is a first draft of its implementation:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">PickerButton</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">index</span><span class="p">:</span> <span class="kt">Int</span>
    <span class="k">let</span> <span class="nv">title</span><span class="p">:</span> <span class="kt">String</span>
   <span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">selectedButtonIndex</span><span class="p">:</span> <span class="kt">Int</span>
    
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">Button</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="p">{</span>
            <span class="n">selectedButtonIndex</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">index</span>
        <span class="p">})</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">title</span><span class="p">)</span><span class="o">.</span><span class="nf">fixedSize</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">padding</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">index</code> holds the index of the segment in the parent view (the segmented control), and the <code class="language-plaintext highlighter-rouge">title</code> is used for displaying the title of the segment. The view’s body is very simple: a single button that displays the title and sets the <code class="language-plaintext highlighter-rouge">selectedButtonIndex</code> when tapped. The <code class="language-plaintext highlighter-rouge">selectedButtonIndex</code> is a binding so that we can communicate the segment selection back to the control.</p>

<p>Now we are ready to start implementing the segmented control. We’ll start by implementing the basic control, without any fancy, flexibility oriented features. We’ll need to hold an array of titles to use for the button labels, and we’ll need to know which button represents the selected segment. The <code class="language-plaintext highlighter-rouge">selectedSegment</code> is a <code class="language-plaintext highlighter-rouge">@Binding</code> so that we can communicate, not only between the buttons and the picker, but also between the parent view that probably has a <code class="language-plaintext highlighter-rouge">@State</code> var to listen to selection changes in the picker.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">SegmentedPicker</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">selectedSegment</span><span class="p">:</span> <span class="kt">Int</span>
    <span class="k">var</span> <span class="nv">labels</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">HStack</span> <span class="p">{</span>
            <span class="kt">ForEach</span><span class="p">(</span><span class="mi">0</span> <span class="o">..&lt;</span> <span class="n">labels</span><span class="o">.</span><span class="n">count</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">PickerButton</span><span class="p">(</span><span class="nv">index</span><span class="p">:</span> <span class="nv">$0</span><span class="p">,</span> <span class="nv">title</span><span class="p">:</span> <span class="n">labels</span><span class="p">[</span><span class="nv">$0</span><span class="p">],</span> <span class="nv">selectedButtonIndex</span><span class="p">:</span> <span class="err">$</span><span class="n">selectedSegment</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since we want to lay out our content horizontally, we start out with an <code class="language-plaintext highlighter-rouge">HStack</code>, and within it, we loop through the labels, and create a <code class="language-plaintext highlighter-rouge">PickerButton</code> for each, providing it its title and index, and also passing in the binding to the active index.</p>

<p>If you create this picker in a project or playground, you’ll end up with the three buttons arranged horizontally on a single line. Here’s a sample implementation of the contentview in such a project, with some padding and spacers to make it look nice:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">selectedSegment</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">1</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span> <span class="p">{</span>
            <span class="kt">Spacer</span><span class="p">()</span>
            <span class="kt">SegmentedPicker</span><span class="p">(</span><span class="nv">selectedSegment</span><span class="p">:</span> <span class="err">$</span><span class="n">selectedSegment</span><span class="p">,</span> <span class="nv">labels</span><span class="p">:</span> <span class="p">[</span><span class="s">"Option 1"</span><span class="p">,</span> <span class="s">"Option 2"</span><span class="p">,</span> <span class="s">"Option 3"</span><span class="p">],</span> <span class="nv">markerHeight</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
            <span class="kt">Spacer</span><span class="p">()</span>
            <span class="kt">Text</span><span class="p">(</span><span class="s">"Current Option: </span><span class="se">\(</span><span class="n">selectedSegment</span> <span class="o">+</span> <span class="mi">1</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
            <span class="kt">Spacer</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Feel free to try this out.</p>

<p>Tapping a button will set the picker’s <code class="language-plaintext highlighter-rouge">selectedSegment</code> to its index, but the picker won’t yet show the selected segment, so that’s the first addition we’ll make to the code above.</p>

<p>The <code class="language-plaintext highlighter-rouge">SegmentedPicker</code> will be responsible for drawing the beam underneath the selected segment. For this it needs to know the bounds of each segment. In SwiftUI we can use an <code class="language-plaintext highlighter-rouge">anchorPreference</code> on the <code class="language-plaintext highlighter-rouge">PickerButton</code> to communicate these bounds back up the view hierarchy. (We’ll also use these bounds to figure out the total width of the segments, and then compare that total to the width of the container view, but more on that later.)</p>

<p>Before we can add an <code class="language-plaintext highlighter-rouge">anchorPreference</code> to the PickerButton, we need to define a <code class="language-plaintext highlighter-rouge">PreferenceKey</code> to hold the bounds information.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ButtonPreferenceData</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">buttonIndex</span><span class="p">:</span> <span class="kt">Int</span>
    <span class="k">var</span> <span class="nv">bounds</span><span class="p">:</span> <span class="kt">Anchor</span><span class="o">&lt;</span><span class="kt">CGRect</span><span class="o">&gt;</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">ButtonPreferenceKey</span><span class="p">:</span> <span class="kt">PreferenceKey</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">defaultValue</span><span class="p">:</span> <span class="p">[</span><span class="kt">ButtonPreferenceData</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="kd">static</span> <span class="kd">func</span> <span class="nf">reduce</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="k">inout</span> <span class="p">[</span><span class="kt">ButtonPreferenceData</span><span class="p">],</span> <span class="nv">nextValue</span><span class="p">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">ButtonPreferenceData</span><span class="p">])</span> <span class="p">{</span>
        <span class="n">value</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="nv">contentsOf</span><span class="p">:</span> <span class="nf">nextValue</span><span class="p">())</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">ButtonPreferenceData</code> struct will hold the info we need: the index of the button (segment), and its bounds. The <code class="language-plaintext highlighter-rouge">reduce</code> function in the <code class="language-plaintext highlighter-rouge">ButtonPreferenceKey</code> ensures we collect this data from all segments.</p>

<p>To actually collect this data we need to add  an <code class="language-plaintext highlighter-rouge">anchorPreference</code> to <code class="language-plaintext highlighter-rouge">PickerButton</code>. Making the whole (and final) version of <code class="language-plaintext highlighter-rouge">PickerButton</code> look like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">PickerButton</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">index</span><span class="p">:</span> <span class="kt">Int</span>
    <span class="k">let</span> <span class="nv">title</span><span class="p">:</span> <span class="kt">String</span>
    <span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">selectedButtonIndex</span><span class="p">:</span> <span class="kt">Int</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">Button</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="p">{</span>
            <span class="n">selectedButtonIndex</span> <span class="o">=</span> <span class="n">index</span>
        <span class="p">})</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">title</span><span class="p">)</span><span class="o">.</span><span class="nf">fixedSize</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">padding</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
        <span class="o">.</span><span class="nf">anchorPreference</span><span class="p">(</span><span class="nv">key</span><span class="p">:</span> <span class="kt">ButtonPreferenceKey</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="o">.</span><span class="n">bounds</span><span class="p">,</span> <span class="nv">transform</span><span class="p">:</span> <span class="p">{</span>
            <span class="p">[</span><span class="kt">ButtonPreferenceData</span><span class="p">(</span><span class="nv">buttonIndex</span><span class="p">:</span> <span class="n">index</span><span class="p">,</span> <span class="nv">bounds</span><span class="p">:</span> <span class="nv">$0</span><span class="p">)]</span>
        <span class="p">})</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the <code class="language-plaintext highlighter-rouge">anchorPreference</code> we create an instance of <code class="language-plaintext highlighter-rouge">ButtonPreferenceData</code> and fill it with the index and bounds of the segment it represents. Finally, we return it wrapped in an array, since that is what our implementation of the reduce function expects.</p>

<p>If you did create a project and ran the code earlier, as we encouraged you to do, and saw the three segments, then, if you run the project again now, you will find nothing has changed visually. All we did was add in the plumbing that will enable us to draw the selection indicator, and figure out whether to display a compact or a regular view.</p>

<p>Let’s start with implementing the selection indicator. For that we need to get access to the data we collected in the <code class="language-plaintext highlighter-rouge">anchorPreference</code> on the <code class="language-plaintext highlighter-rouge">PickerButton</code>. We do that by adding a <code class="language-plaintext highlighter-rouge">backgroundPreferenceValue</code> view after the <code class="language-plaintext highlighter-rouge">frame</code> modifier in the body of <code class="language-plaintext highlighter-rouge">SegmentedPicker</code>, where we use a <code class="language-plaintext highlighter-rouge">GeometryReader</code> to obtain the bounds of the picker’s container. Once we have that, we pass it, along with the collected preference data, to a function that will either return an indicator view or an empty view if it can’t find a selection. We also need a height for the indicator. For that we’ll add a user accessible instance variable, with a default value. If you run the project with the incarnation of SegmentedPicker shown below, you’ll now see a selection indicator. If you tap another segment, the indicator animates into place to indicate the changed selection.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">SegmentedPicker</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@Binding</span> <span class="k">var</span> <span class="nv">selectedSegment</span><span class="p">:</span> <span class="kt">Int</span>
    <span class="k">var</span> <span class="nv">labels</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>
    <span class="k">var</span> <span class="nv">markerHeight</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">6</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">HStack</span> <span class="p">{</span>
            <span class="kt">ForEach</span><span class="p">(</span><span class="mi">0</span> <span class="o">..&lt;</span> <span class="n">labels</span><span class="o">.</span><span class="n">count</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">PickerButton</span><span class="p">(</span><span class="nv">index</span><span class="p">:</span> <span class="nv">$0</span><span class="p">,</span> <span class="nv">title</span><span class="p">:</span> <span class="n">labels</span><span class="p">[</span><span class="nv">$0</span><span class="p">],</span> <span class="nv">selectedButtonIndex</span><span class="p">:</span> <span class="err">$</span><span class="n">selectedSegment</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">backgroundPreferenceValue</span><span class="p">(</span><span class="kt">ButtonPreferenceKey</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">preferences</span> <span class="k">in</span>
            <span class="kt">GeometryReader</span> <span class="p">{</span> <span class="n">containerGeometry</span> <span class="k">in</span>
                <span class="nf">showSelectionIndicator</span><span class="p">(</span><span class="nv">containerGeometry</span><span class="p">:</span> <span class="n">containerGeometry</span><span class="p">,</span> <span class="nv">preferences</span><span class="p">:</span> <span class="n">preferences</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">showSelectionIndicator</span><span class="p">(</span><span class="nv">containerGeometry</span><span class="p">:</span> <span class="kt">GeometryProxy</span><span class="p">,</span> <span class="nv">preferences</span><span class="p">:</span> <span class="p">[</span><span class="kt">ButtonPreferenceData</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">preference</span> <span class="o">=</span> <span class="n">preferences</span><span class="o">.</span><span class="nf">first</span><span class="p">(</span><span class="nv">where</span><span class="p">:</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">buttonIndex</span> <span class="o">==</span> <span class="k">self</span><span class="o">.</span><span class="n">selectedSegment</span> <span class="p">})</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">anchorBounds</span> <span class="o">=</span> <span class="n">preference</span><span class="o">.</span><span class="n">bounds</span>
            <span class="k">let</span> <span class="nv">bounds</span> <span class="o">=</span> <span class="n">containerGeometry</span><span class="p">[</span><span class="n">anchorBounds</span><span class="p">]</span>
            <span class="k">return</span> <span class="kt">AnyView</span><span class="p">(</span><span class="kt">RoundedRectangle</span><span class="p">(</span><span class="nv">cornerRadius</span><span class="p">:</span> <span class="n">markerHeight</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
                            <span class="o">.</span><span class="nf">fill</span><span class="p">()</span>
                            <span class="o">.</span><span class="nf">foregroundColor</span><span class="p">(</span><span class="kt">Color</span><span class="o">.</span><span class="n">yellow</span><span class="p">)</span>
                            <span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="n">bounds</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="n">markerHeight</span><span class="p">)</span>
                            <span class="o">.</span><span class="nf">offset</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="n">bounds</span><span class="o">.</span><span class="n">minX</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="n">bounds</span><span class="o">.</span><span class="n">maxY</span><span class="p">)</span>
                            <span class="o">.</span><span class="nf">animation</span><span class="p">(</span><span class="o">.</span><span class="nf">easeInOut</span><span class="p">(</span><span class="nv">duration</span><span class="p">:</span> <span class="mf">0.33</span><span class="p">)))</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kt">AnyView</span><span class="p">(</span><span class="kt">EmptyView</span><span class="p">())</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To know where to place the selection indicator, we first extract the selected segment from the preferences we received. Then we access its bounds, and resolve the value of the anchor to the container view. Now that we have the bounds in the correct coordinate space, building the indicator is fairly straightforward.</p>

<p>With the code above you now have a fully functioning custom segmented picker, which supports dark mode and dynamic font sizes out of the box. However, on smaller screens, and even on iPads when multitasking with a split screen, and, of course, depending on the number of segments and the length of their titles, it may we happen that the outer segments get clipped if the available screen real estate is not wide enough. When this happens we want the picker to morph into a more compact, popup style control, which only shows the selected option, and allows the user to choose another option by tapping and holding the button, and then selecting from the menu that pops up.</p>

<p>To know when to show the popup style instead of the regular style we need to figure out the total width of all the segments (including their padding), and compare that to the width available to the picker. Fortunately, we already have the bounds of each segment, which we needed earlier for displaying the selection indicator. So there’s no need to add any code for that. Yay! But we do need to gain access to the picker’s surrounding geometry so that we can decide on which view to show. We will change the <code class="language-plaintext highlighter-rouge">body</code> of the picker view to gather geometry info, and will pass that along to helper functions so that they can make informed decisions.</p>

<p>In <code class="language-plaintext highlighter-rouge">SegementedPicker</code> replace the definition of the <code class="language-plaintext highlighter-rouge">body</code> with the following</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">GeometryReader</span> <span class="p">{</span> <span class="n">stackProxy</span> <span class="k">in</span>
            <span class="nf">appropriateView</span><span class="p">(</span><span class="nv">stackProxy</span><span class="p">:</span> <span class="n">stackProxy</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>We now return a GeometryReader, which gives us access to data like the bounds available to the picker. We will modularize the code into small helper functions that return some appropriate view. This modularization not only helps in keeping the code clear and manageable, it also helps a very picky compiler, prone to complain about returning views of different types.</p>

<p>When deciding which view to show—the regular one, or the compact one—we immediately run into a challenge: we can’t measure the required width before building the view, and then just show the appropriate one, as we could have done in UIKit. We have to actually start building the regular view tree, and then switch to the compact view if we find the regular view won’t fit. But when we re-render the tree for the compact view, we cannot know the required width, so really we need to first build the regular view every time. But then, if we switch to compact again, we end up with an infinite loop. We need to break this cycle by only showing the compact view if we just re-entered from a regular pass through the layout process, and otherwise build the regular view. This ensures that, if we are showing the compact view, we will always re-evaluate by trying to build the regular view.</p>

<p>To keep track of whether we just switched to the compact view in the previous layout pass, we add an instance variable to the picker. We use this variable to decide which view to return:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">appropriateView</span><span class="p">(</span><span class="nv">stackProxy</span><span class="p">:</span> <span class="kt">GeometryProxy</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">justSwitchedToCompactView</span> <span class="p">{</span>
            <span class="n">justSwitchedToCompactView</span> <span class="o">=</span> <span class="kc">false</span>
            <span class="k">return</span> <span class="kt">AnyView</span><span class="p">(</span><span class="nf">compactView</span><span class="p">(</span><span class="nv">stackProxy</span><span class="p">:</span> <span class="n">stackProxy</span><span class="p">))</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kt">AnyView</span><span class="p">(</span><span class="nf">regularView</span><span class="p">(</span><span class="nv">stackProxy</span><span class="p">:</span> <span class="n">stackProxy</span><span class="p">))</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>Unfortunately, as this is a struct, we can’t easily modify this variable, as the compiler will complain that <code class="language-plaintext highlighter-rouge">self</code> is not modifiable. We could turn it into a <code class="language-plaintext highlighter-rouge">@State</code> variable, but then changing it would always trigger a new layout pass, which wouldn’t work either. Yet we need to ensure the variable becomes modifiable from within the <code class="language-plaintext highlighter-rouge">SegmentedPicker</code>. Custom property wrappers to the rescue:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@propertyWrapper</span> <span class="kd">class</span> <span class="kt">Modifiable</span><span class="o">&lt;</span><span class="kt">Value</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Value</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">Value</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">wrappedValue</span>
    <span class="p">}</span>
    
    <span class="k">var</span> <span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">Value</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="n">value</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span> <span class="n">value</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The above property wrapper allows us to easily add a modifiable instance variable to a struct:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">SegmentedPicker</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@Modifiable</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">justSwitchedToCompactView</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">false</span>
<span class="err">…</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now the compiler is happy, and so are we. :-)</p>

<p>Next, let’s first get the implementation of the compact view out of the way, since it is more straightforward than the regular view’s implementation.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">compactView</span><span class="p">(</span><span class="nv">stackProxy</span><span class="p">:</span> <span class="kt">GeometryProxy</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">validMenuItems</span> <span class="o">=</span> <span class="n">labels</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">labels</span><span class="p">[</span><span class="n">selectedSegment</span><span class="p">]</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="kt">HStack</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">labels</span><span class="p">[</span><span class="n">selectedSegment</span><span class="p">])</span><span class="o">.</span><span class="nf">foregroundColor</span><span class="p">(</span><span class="kt">Color</span><span class="p">(</span><span class="kt">UIColor</span><span class="o">.</span><span class="n">label</span><span class="p">))</span>
            <span class="kt">Image</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="s">"arrowtriangle.down.circle"</span><span class="p">)</span><span class="o">.</span><span class="nf">foregroundColor</span><span class="p">(</span><span class="kt">Color</span><span class="p">(</span><span class="kt">UIColor</span><span class="o">.</span><span class="n">label</span><span class="p">))</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
        <span class="o">.</span><span class="nf">background</span><span class="p">(</span><span class="kt">Color</span><span class="p">(</span><span class="kt">UIColor</span><span class="o">.</span><span class="n">systemBackground</span><span class="p">))</span>
        <span class="o">.</span><span class="nf">cornerRadius</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
        <span class="o">.</span><span class="n">contextMenu</span> <span class="p">{</span>
            <span class="kt">ForEach</span><span class="p">(</span><span class="n">validMenuItems</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">menuItem</span> <span class="k">in</span>
                <span class="kt">Button</span><span class="p">(</span><span class="n">menuItem</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">selectedSegment</span> <span class="o">=</span> <span class="n">labels</span><span class="o">.</span><span class="nf">firstIndex</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="n">menuItem</span><span class="p">)</span><span class="o">!</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="n">stackProxy</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="n">stackProxy</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span><span class="p">,</span> <span class="nv">alignment</span><span class="p">:</span> <span class="o">.</span><span class="n">center</span><span class="p">)</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>We do not want to show the currently selected option in the menu, so we first create an array with the selected segment filtered out. We show the selected segment along with a little icon that suggests you can tap it, through a <code class="language-plaintext highlighter-rouge">Text</code> and an <code class="language-plaintext highlighter-rouge">Image</code> wrapped in an <code class="language-plaintext highlighter-rouge">HStack</code>. We provide some formatting modifiers so that it looks right, especially when the popup menu is shown (and make sure it plays nicely with both dark and light mode by using the <code class="language-plaintext highlighter-rouge">systemBackground</code> color). Finally we add the <code class="language-plaintext highlighter-rouge">contextMenu</code> modifier, where we build the menu options by cycling through the valid menu items.</p>

<p>With the compact view covered, we are ready to focus on the regular view. Most of the code here you’ve seen before:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">regularView</span><span class="p">(</span><span class="nv">stackProxy</span><span class="p">:</span> <span class="kt">GeometryProxy</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">HStack</span> <span class="p">{</span>
            <span class="kt">ForEach</span><span class="p">(</span><span class="mi">0</span> <span class="o">..&lt;</span> <span class="n">labels</span><span class="o">.</span><span class="n">count</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">PickerButton</span><span class="p">(</span><span class="nv">index</span><span class="p">:</span> <span class="nv">$0</span><span class="p">,</span> <span class="nv">title</span><span class="p">:</span> <span class="n">labels</span><span class="p">[</span><span class="nv">$0</span><span class="p">],</span> <span class="nv">selectedButtonIndex</span><span class="p">:</span> <span class="err">$</span><span class="n">selectedSegment</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="n">stackProxy</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="n">stackProxy</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span><span class="p">,</span> <span class="nv">alignment</span><span class="p">:</span> <span class="o">.</span><span class="n">center</span><span class="p">)</span>
        <span class="o">.</span><span class="nf">backgroundPreferenceValue</span><span class="p">(</span><span class="kt">ButtonPreferenceKey</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">preferences</span> <span class="k">in</span>
            <span class="nf">processPreferenceValue</span><span class="p">(</span><span class="nv">containerGeometry</span><span class="p">:</span> <span class="n">stackProxy</span><span class="p">,</span> <span class="nv">preferences</span><span class="p">:</span> <span class="n">preferences</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">regularView</code> function builds the segments using an HStack, looping through all labels to create a <code class="language-plaintext highlighter-rouge">PickerButton</code> for each. Each button is provided with its title, its index and a binding to the selectedSegment, so that it can communicate a new selection back up when it is tapped. We make sure the HStack is centered within its parent view by setting its frame size to equal the parent’s size.</p>

<p>Now it’s time to figure out if the regular view fits its parent, and take action accordingly. Triggering a new layout pass if it doesn’t fit, or showing a selection indicator beneath the selected segment if it does.</p>

<p>This happens within the <code class="language-plaintext highlighter-rouge">backgroundPreferenceValue</code> modifier, where we have access to the collected geometry data for each segment. The code is extracted into the <code class="language-plaintext highlighter-rouge">processPreferenceValue</code> function to keep our code nicely modularized, as well as to help the compiler figure out the view types.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">processPreferenceValue</span><span class="p">(</span><span class="nv">containerGeometry</span><span class="p">:</span> <span class="kt">GeometryProxy</span><span class="p">,</span> <span class="nv">preferences</span><span class="p">:</span> <span class="p">[</span><span class="kt">ButtonPreferenceData</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">fits</span> <span class="o">=</span> <span class="nf">fitsContainer</span><span class="p">(</span><span class="nv">containerGeometry</span><span class="p">:</span> <span class="n">containerGeometry</span><span class="p">,</span> <span class="nv">preferences</span><span class="p">:</span> <span class="n">preferences</span><span class="p">)</span>
        <span class="nf">switchViewIfNeeded</span><span class="p">(</span><span class="nv">fits</span><span class="p">:</span> <span class="n">fits</span><span class="p">)</span>

        <span class="k">return</span> <span class="kt">Group</span> <span class="p">{</span>
            <span class="k">if</span>  <span class="n">fits</span> <span class="p">{</span>
                <span class="nf">showSelectionIndicator</span><span class="p">(</span><span class="nv">containerGeometry</span><span class="p">:</span> <span class="n">containerGeometry</span><span class="p">,</span> <span class="nv">preferences</span><span class="p">:</span> <span class="n">preferences</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">maxWidth</span><span class="p">:</span> <span class="o">.</span><span class="n">infinity</span><span class="p">,</span> <span class="nv">maxHeight</span><span class="p">:</span> <span class="o">.</span><span class="n">infinity</span><span class="p">,</span> <span class="nv">alignment</span><span class="p">:</span> <span class="o">.</span><span class="n">topLeading</span><span class="p">)</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="kt">EmptyView</span><span class="p">()</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">processPreferenceValue</code> first checks if the regular view fits its container. Then it calls a function that may trigger a new layout pass, depending on its current state and the state it needs to be in. Finally, if the regular view doesn’t fit, it will return an empty view, since we don’t need to show anything when in compact mode, and, if the regular view does fit, it will return a selection indicator. We need to envelop the returned views in a <code class="language-plaintext highlighter-rouge">Group</code> to keep the compiler happy. (The alternative being to return the views wrapped in an <code class="language-plaintext highlighter-rouge">AnyView</code>, but, for performance reasons, using a group is preferable where possible.)</p>

<p>The implementation of <code class="language-plaintext highlighter-rouge">fitsContainer</code> collects the combined width of each segment in the reduce function, and returns a bool indicating whether that width fits within the container:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">fitsContainer</span><span class="p">(</span><span class="nv">containerGeometry</span><span class="p">:</span> <span class="kt">GeometryProxy</span><span class="p">,</span> <span class="nv">preferences</span><span class="p">:</span> <span class="p">[</span><span class="kt">ButtonPreferenceData</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">requiredWidth</span> <span class="o">=</span> <span class="n">preferences</span><span class="o">.</span><span class="nf">reduce</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">pref</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
            <span class="k">let</span> <span class="nv">anchorBounds</span> <span class="o">=</span> <span class="n">pref</span><span class="o">.</span><span class="n">bounds</span>
            <span class="k">let</span> <span class="nv">bounds</span> <span class="o">=</span> <span class="n">containerGeometry</span><span class="p">[</span><span class="n">anchorBounds</span><span class="p">]</span>
            <span class="k">return</span> <span class="n">result</span> <span class="o">+</span> <span class="n">bounds</span><span class="o">.</span><span class="n">width</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="n">requiredWidth</span> <span class="o">&lt;=</span> <span class="n">containerGeometry</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>On to the implementation of <code class="language-plaintext highlighter-rouge">switchViewIfNeeded</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">switchViewIfNeeded</span><span class="p">(</span><span class="nv">fits</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">fits</span> <span class="p">{</span>
            <span class="n">justSwitchedToCompactView</span> <span class="o">=</span> <span class="kc">false</span>
            <span class="k">if</span> <span class="o">!</span><span class="n">allFit</span> <span class="p">{</span>
                <span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
                    <span class="n">allFit</span> <span class="o">=</span> <span class="kc">true</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">if</span> <span class="n">allFit</span> <span class="p">{</span>
                <span class="n">justSwitchedToCompactView</span> <span class="o">=</span> <span class="kc">true</span>
                <span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
                    <span class="n">allFit</span> <span class="o">=</span> <span class="kc">false</span>
                <span class="p">}</span>
            <span class="p">}</span>  <span class="k">else</span> <span class="p">{</span>
                <span class="n">justSwitchedToCompactView</span> <span class="o">=</span> <span class="kc">false</span>
                <span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
                    <span class="n">allFit</span> <span class="o">=</span> <span class="kc">true</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>If the regular view fits, we check our <code class="language-plaintext highlighter-rouge">allFit</code> state variable, and set it to true if needed, which will cause the view tree to be re-rendered. We set <code class="language-plaintext highlighter-rouge">justSwitchedToCompactView</code> to <code class="language-plaintext highlighter-rouge">false</code>, since we are not rendering the compact view. We cannot interrupt the layout process at this point without SwiftUI landing us in undefined behaviour territory, so we need to trigger the new layout on the next iteration through the run-loop, by wrapping the code in an <code class="language-plaintext highlighter-rouge">async</code> call on the main queue.</p>

<p>If the regular view doesn’t fit, we check whether we were already showing the compact view. If we were not, we are switching to the compact view, so we set <code class="language-plaintext highlighter-rouge">justSwitchedToCompactView</code> to <code class="language-plaintext highlighter-rouge">true</code>, and set <code class="language-plaintext highlighter-rouge">allFit</code> to false, to trigger a redraw.</p>

<p>If at this point the compact view was already being shown, the required width has not been calculated, and we need to trigger a new layout pass, that will first try to render the regular view. So we, unintuitively, have to set <code class="language-plaintext highlighter-rouge">allFit</code> to <code class="language-plaintext highlighter-rouge">true</code>to trigger this new pass. (And we ensure <code class="language-plaintext highlighter-rouge">justSwitchedToCompactView</code> is set to false, since we were already showing the compact view, and, more importantly, otherwise the path to try to render the regular view, in the <code class="language-plaintext highlighter-rouge">appropriateView</code> function, will not be taken.)</p>

<p>With all this in place, we now have a fully functioning segmented control that adapts its display mode to the space it’s given, depending on the space its regular view needs.</p>

<p>You can find the full source code <a href="https://github.com/SintraWorks/SegmentedPicker/">here</a>.</p>]]></content><author><name></name></author><category term="swift" /><category term="swiftui" /><category term="swift" /><category term="swifui" /><summary type="html"><![CDATA[A segmented control, built in SwiftUI, that doubles as a popup menu when space is tight.]]></summary></entry><entry><title type="html">Fractions</title><link href="https://sintraworks.github.io/fractions/math/swift/2020/03/18/fractions.html" rel="alternate" type="text/html" title="Fractions" /><published>2020-03-18T00:00:00+00:00</published><updated>2020-03-18T00:00:00+00:00</updated><id>https://sintraworks.github.io/fractions/math/swift/2020/03/18/fractions</id><content type="html" xml:base="https://sintraworks.github.io/fractions/math/swift/2020/03/18/fractions.html"><![CDATA[<p>Sometimes, using floating point arithmetic (e.g using float or double types) just doesn’t cut it. Floating point values cannot represent all values accurately, and if you start adding/subtracting/multiplying/dividing such values it is very likely the inacurracies quickly exacerbate into an unworkable mess. Depending on the domain you’re working on, different solutions can be appropriate. E.g, if you’re working with currency, you might need a type representing decimal numbers, or, if you’re working with musical timelines or scores, especially where tuplets (e.g. triplets) come into the mix, a type accurately representing any fraction may be called for. Here we will look into the latter: a type where each instance represents a fraction. We want to be able to perform basic arthmetic calculations on those numbers.</p>

<p>Since we’re being Swifty, but more importantly, since we’re going to represent fairly basic values, and care about (im)mutability, we’ll use a value type to represent our fractions: a <code class="language-plaintext highlighter-rouge">struct</code>.</p>

<p>Fractions have just two properties: a <code class="language-plaintext highlighter-rouge">denominator</code> –representing the value’s base unit– and a <code class="language-plaintext highlighter-rouge">numerator</code> –expressing how many units the value considers–.</p>

<p>The result:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Fraction</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">numerator</span><span class="p">:</span> <span class="kt">Int</span>
    <span class="k">var</span> <span class="nv">denominator</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What’s next? We want to be able to instantiate instances of fractions. Since we are using Swift structs, we do not <em>need</em> to write an initializer, since one will be automatically synthesized for us. But we do want to do perform some basic input checking in our initializer, to ensure we end up with a reasonably safe instance. For starters, division by zero is illegal. We therefore need to check that the denominator is not zero. Secondly, since we will implement an essential function that flips negative values to their positive counterpart, we cannot allow the use of <code class="language-plaintext highlighter-rouge">Int.min</code>, and have to check that <code class="language-plaintext highlighter-rouge">parameters &gt; Int.min</code>. We could create a throwing initializer, and throw an error when invalid parameter values are provided, but I think that is overkill for the task at hand, and might make the initializer less convenient at the point of use. Instead, we’ll make the initializer failable:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">init</span><span class="p">?(</span><span class="nv">numerator</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">guard</span> <span class="n">denominator</span> <span class="o">!=</span> <span class="mi">0</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span>
    <span class="k">guard</span> <span class="n">numerator</span> <span class="o">&gt;</span> <span class="kt">Int</span><span class="o">.</span><span class="n">min</span><span class="p">,</span> <span class="n">denominator</span> <span class="o">&gt;</span> <span class="kt">Int</span><span class="o">.</span><span class="n">min</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span>

    <span class="k">self</span><span class="o">.</span><span class="n">numerator</span> <span class="o">=</span> <span class="n">numerator</span>
    <span class="k">self</span><span class="o">.</span><span class="n">denominator</span> <span class="o">=</span> <span class="n">denominator</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, before we start implementing arithmetic operations on the type, lets get a useful utility out of the way. It is likely we will encounter occasions where we need to convert our fraction into a floating point representation:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">doubleValue</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span>
    <span class="kt">Double</span><span class="p">(</span><span class="n">numerator</span><span class="p">)</span> <span class="o">/</span> <span class="kt">Double</span><span class="p">(</span><span class="n">denominator</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">var</span> <span class="nv">floatValue</span><span class="p">:</span> <span class="kt">Float</span> <span class="p">{</span>
    <span class="kt">Float</span><span class="p">(</span><span class="n">doubleValue</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>(Remember: in Swift 5.1 and later we do not have to write <code class="language-plaintext highlighter-rouge">return</code> when the function body consists of a single expression.)</p>

<p>We also want a compact way to print our fractions:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">description</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
    <span class="s">"</span><span class="se">\(</span><span class="n">numerator</span><span class="se">)</span><span class="s">/</span><span class="se">\(</span><span class="n">denominator</span><span class="se">)</span><span class="s">"</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With that out of the way, it’s time to implement our first operation: addition. We want both mutating and nonmutating variants.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">mutating</span> <span class="kd">func</span> <span class="nf">add</span><span class="p">(</span><span class="n">_</span> <span class="nv">other</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">denominator</span> <span class="o">==</span> <span class="n">other</span><span class="o">.</span><span class="n">denominator</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">numerator</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">numerator</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">numerator</span> <span class="o">=</span> <span class="n">numerator</span> <span class="o">*</span> <span class="n">other</span><span class="o">.</span><span class="n">denominator</span> <span class="o">+</span> <span class="n">other</span><span class="o">.</span><span class="n">numerator</span> <span class="o">*</span> <span class="n">denominator</span>
        <span class="n">denominator</span> <span class="o">=</span> <span class="n">denominator</span> <span class="o">*</span> <span class="n">other</span><span class="o">.</span><span class="n">denominator</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">adding</span><span class="p">(</span><span class="n">_</span> <span class="nv">other</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Fraction</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">copy</span> <span class="o">=</span> <span class="k">self</span>
    <span class="n">copy</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="nv">reducing</span><span class="p">:</span> <span class="n">reducing</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">copy</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now we can create two fractions and add them together:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">f2_4</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">f1_2</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span><span class="o">!</span>
<span class="n">f2_4</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">f1_2</span><span class="p">)</span>
<span class="n">f2_4</span><span class="o">.</span><span class="n">description</span> <span class="c1">// 8/8</span>
</code></pre></div></div>

<p>Nice! The result is correct. But it is probably not the value we were looking for. It is desirable to be able to reduce fractions to their GCD (Greatest Common Denominator/Divisor). To enable us to do that, we implement a function we’ll, unsurprisingly, call <code class="language-plaintext highlighter-rouge">reduce</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">mutating</span> <span class="kd">func</span> <span class="nf">reduce</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">u</span> <span class="o">=</span> <span class="n">numerator</span>
    <span class="k">var</span> <span class="nv">v</span> <span class="o">=</span> <span class="n">denominator</span>

    <span class="c1">// Euclid's solution to finding the Greatest Common Denominator</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">u</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">u</span> <span class="o">%</span> <span class="n">v</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="n">numerator</span> <span class="o">/=</span> <span class="n">u</span>
    <span class="n">denominator</span> <span class="o">/=</span> <span class="n">u</span>
<span class="p">}</span>


<span class="kd">func</span> <span class="nf">reduced</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Fraction</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">copy</span> <span class="o">=</span> <span class="k">self</span>
    <span class="n">copy</span><span class="o">.</span><span class="nf">reduce</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">copy</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We change the <code class="language-plaintext highlighter-rouge">add</code> functions to take a parameter indicating whether we want to reduce the result of the operation before returning it. We’ll assume we usually want to do that, so we default to <code class="language-plaintext highlighter-rouge">true</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">mutating</span> <span class="kd">func</span> <span class="nf">add</span><span class="p">(</span><span class="n">_</span> <span class="nv">other</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">,</span> <span class="nv">reducing</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">denominator</span> <span class="o">==</span> <span class="n">other</span><span class="o">.</span><span class="n">denominator</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">numerator</span> <span class="o">+=</span> <span class="n">other</span><span class="o">.</span><span class="n">numerator</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">numerator</span> <span class="o">=</span> <span class="n">numerator</span> <span class="o">*</span> <span class="n">other</span><span class="o">.</span><span class="n">denominator</span> <span class="o">+</span> <span class="n">other</span><span class="o">.</span><span class="n">numerator</span> <span class="o">*</span> <span class="n">denominator</span>
        <span class="n">denominator</span> <span class="o">=</span> <span class="n">denominator</span> <span class="o">*</span> <span class="n">other</span><span class="o">.</span><span class="n">denominator</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">reducing</span> <span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">reduce</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, lets run the sample addition above again:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">f2_4</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">f1_2</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span><span class="o">!</span>
<span class="n">f2_4</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">f1_2</span><span class="p">)</span>
<span class="n">f2_4</span><span class="o">.</span><span class="n">description</span> <span class="c1">// 1/1</span>
</code></pre></div></div>

<p>Great! And if we want to prevent the reduction:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">f2_4</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">f1_2</span><span class="p">,</span> <span class="nv">reducing</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
<span class="n">f2_4</span><span class="o">.</span><span class="n">description</span> <span class="c1">// 8/8</span>
</code></pre></div></div>

<p>There’s a problem lurking in our <code class="language-plaintext highlighter-rouge">reduce</code> implementation though. You’ll discover that, if you write unit tests that exercise that function with fractions that contain negative values:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">f1</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">15</span><span class="p">)</span><span class="o">!</span>
<span class="n">f1</span><span class="o">.</span><span class="nf">reduce</span><span class="p">()</span>
<span class="n">f1</span><span class="o">.</span><span class="n">description</span> <span class="c1">// 1/-5</span>
</code></pre></div></div>

<p>The correct answer should be -1/5! Looks like we need to refine our <code class="language-plaintext highlighter-rouge">reduce</code> function.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">mutating</span> <span class="kd">func</span> <span class="nf">reduce</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="p">(</span><span class="nv">absNumerator</span><span class="p">,</span> <span class="nv">numeratorSign</span><span class="p">)</span> <span class="o">=</span> <span class="n">numerator</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="p">(</span><span class="o">-</span><span class="n">numerator</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">:</span> <span class="p">(</span><span class="n">numerator</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="k">let</span> <span class="p">(</span><span class="nv">absDenominator</span><span class="p">,</span> <span class="nv">denominatorSign</span><span class="p">)</span> <span class="o">=</span> <span class="n">denominator</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="p">(</span><span class="o">-</span><span class="n">denominator</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">:</span> <span class="p">(</span><span class="n">denominator</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>

    <span class="k">var</span> <span class="nv">u</span> <span class="o">=</span> <span class="n">absNumerator</span>
    <span class="k">var</span> <span class="nv">v</span> <span class="o">=</span> <span class="n">absDenominator</span>

    <span class="c1">// Euclid's solution to finding the Greatest Common Denominator</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">u</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">u</span> <span class="o">%</span> <span class="n">v</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="n">numerator</span> <span class="o">=</span> <span class="n">absNumerator</span> <span class="o">/</span> <span class="n">u</span> <span class="o">*</span> <span class="n">numeratorSign</span>
    <span class="n">denominator</span> <span class="o">=</span> <span class="n">absDenominator</span> <span class="o">/</span> <span class="n">u</span> <span class="o">*</span> <span class="n">denominatorSign</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We take the absolute values of the numerator and denominator and perform the reduction on these. When done we re-establish the signedness of each, thus ensuring a correct result:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">f1</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">15</span><span class="p">)</span><span class="o">!</span>
<span class="n">f1</span><span class="o">.</span><span class="nf">reduce</span><span class="p">()</span>
<span class="n">f1</span><span class="o">.</span><span class="n">description</span> <span class="c1">// -1/5</span>
</code></pre></div></div>

<p>The implementations for subtraction, multiplication and division follow the same pattern as the implementation for addition. You can check out the actual implementations in the project’s source. We won’t discuss them here.</p>

<p>So, as we saw above, now we can write</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">f2_4</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">f1_2</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span><span class="o">!</span>
<span class="n">f2_4</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">f1_2</span><span class="p">)</span> <span class="c1">// or: let result = f2_4.adding(1_2)</span>
</code></pre></div></div>
<p>but wouldn’t it be nicer if we could write</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="n">f2_4</span> <span class="o">+</span> <span class="n">f1_2</span>
</code></pre></div></div>

<p>Yes? Then let’s make that possible by overloading the operators. I think this is a domain where doing that makes sense. And, since the implementations are very compact, I’ll list them all here:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="o">+</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Fraction</span> <span class="p">{</span>
    <span class="n">lhs</span><span class="o">.</span><span class="nf">adding</span><span class="p">(</span><span class="n">rhs</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="o">-</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Fraction</span> <span class="p">{</span>
    <span class="n">lhs</span><span class="o">.</span><span class="nf">subtracting</span><span class="p">(</span><span class="n">rhs</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="o">*</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Fraction</span> <span class="p">{</span>
    <span class="n">lhs</span><span class="o">.</span><span class="nf">multiplying</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">rhs</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="o">/</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Fraction</span> <span class="p">{</span>
    <span class="n">lhs</span><span class="o">.</span><span class="nf">dividing</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">rhs</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This allows us to write:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">f2_4</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">f1_2</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="n">f2_4</span> <span class="o">+</span> <span class="mi">1_2</span> <span class="c1">// 1/1</span>
</code></pre></div></div>

<p>Still missing is the ability to compare fractions, so let’s turn our attention to that: To enable comparisons we must conform our Fraction type to the Comparable protocol. This involves implementing two functions: <code class="language-plaintext highlighter-rouge">==</code> and <code class="language-plaintext highlighter-rouge">&lt;</code>. The implementations are fairly standard and simple, but there is one gotcha: Fractions with minus signs in different positions may still be equal. For instance, <code class="language-plaintext highlighter-rouge">1/-4</code> represents the same value as <code class="language-plaintext highlighter-rouge">-1/4</code>; and <code class="language-plaintext highlighter-rouge">-1/-4</code> is the same as <code class="language-plaintext highlighter-rouge">1/4</code>. To account for that we need to ‘normalize’ these fractions to facilitate the implementation of the comparison functions. The rules for normalization are provided as documentation to the normalization methods below:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Fraction</span><span class="p">:</span> <span class="kt">Comparable</span> <span class="p">{</span>
    <span class="c1">/// Fractions with two negative signs are normalized to two positive signs.</span>
    <span class="c1">/// Fractions with negative denominator are normalized to positive denominator and negative numerator.</span>
    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">normalize</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">numerator</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">denominator</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">numerator</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">denominator</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">denominator</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">numerator</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span>
            <span class="n">denominator</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">/// Fractions with two negative signs are normalized to two positive signs.</span>
    <span class="c1">/// Fractions with negative denominator are normalized to positive denominator and negative numerator.</span>
    <span class="kd">func</span> <span class="nf">normalized</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Fraction</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">copy</span> <span class="o">=</span> <span class="k">self</span>
        <span class="n">copy</span><span class="o">.</span><span class="nf">normalize</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">copy</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>With these functions in place, we can safely implement the comparison functions.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kd">func</span> <span class="o">==</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">lhsRed</span> <span class="o">=</span> <span class="n">lhs</span><span class="o">.</span><span class="nf">reduced</span><span class="p">()</span><span class="o">.</span><span class="nf">normalized</span><span class="p">()</span>
    <span class="k">let</span> <span class="nv">rhsRed</span> <span class="o">=</span> <span class="n">rhs</span><span class="o">.</span><span class="nf">reduced</span><span class="p">()</span><span class="o">.</span><span class="nf">normalized</span><span class="p">()</span>

    <span class="k">return</span> <span class="n">lhsRed</span><span class="o">.</span><span class="n">numerator</span> <span class="o">==</span> <span class="n">rhsRed</span><span class="o">.</span><span class="n">numerator</span> <span class="o">&amp;&amp;</span> <span class="n">lhsRed</span><span class="o">.</span><span class="n">denominator</span> <span class="o">==</span> <span class="n">rhsRed</span><span class="o">.</span><span class="n">denominator</span>
<span class="p">}</span>

<span class="kd">static</span> <span class="kd">func</span> <span class="o">&lt;</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">lhsRed</span> <span class="o">=</span> <span class="n">lhs</span><span class="o">.</span><span class="nf">reduced</span><span class="p">()</span><span class="o">.</span><span class="nf">normalized</span><span class="p">()</span>
    <span class="k">let</span> <span class="nv">rhsRed</span> <span class="o">=</span> <span class="n">rhs</span><span class="o">.</span><span class="nf">reduced</span><span class="p">()</span><span class="o">.</span><span class="nf">normalized</span><span class="p">()</span>

    <span class="k">let</span> <span class="nv">lhsNominatorProduct</span> <span class="o">=</span> <span class="n">lhsRed</span><span class="o">.</span><span class="n">numerator</span> <span class="o">*</span> <span class="n">rhsRed</span><span class="o">.</span><span class="n">denominator</span>
    <span class="k">let</span> <span class="nv">rhsNominatorProduct</span> <span class="o">=</span> <span class="n">rhsRed</span><span class="o">.</span><span class="n">numerator</span> <span class="o">*</span> <span class="n">lhsRed</span><span class="o">.</span><span class="n">denominator</span>

    <span class="k">return</span> <span class="n">lhsNominatorProduct</span> <span class="o">&lt;</span> <span class="n">rhsNominatorProduct</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There’s only one issue left now. If we operate on fractions with a negative denominator, the results may surprise us. Which is a euphemistic way of saying that the results may well be incorrect. As the code currently stand, the following unit test will fail:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">f1_m4</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="o">-</span><span class="mi">4</span><span class="p">)</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">f2_4</span> <span class="o">=</span> <span class="kt">Fraction</span><span class="p">(</span><span class="nv">numerator</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">denominator</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span><span class="o">!</span>
<span class="n">f1_m4</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">f2_4</span><span class="p">)</span>
<span class="kt">XCTAssertTrue</span><span class="p">(</span><span class="n">f1_m4</span><span class="o">.</span><span class="n">numerator</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">f1_m4</span><span class="o">.</span><span class="n">denominator</span> <span class="o">==</span> <span class="mi">4</span><span class="p">,</span> <span class="s">"Incorrect result adding </span><span class="se">\(</span><span class="n">f2_4</span><span class="o">.</span><span class="n">description</span><span class="se">)</span><span class="s"> to 1/-4. Expected 1/4, got </span><span class="se">\(</span><span class="n">f1_m4</span><span class="o">.</span><span class="n">description</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<p>The solution to getting the test to pass is to normalize the Fractions before operating on them, as we did in the implementation providing the <code class="language-plaintext highlighter-rouge">Comparable</code> protocol conformance. So now, the implementation of <code class="language-plaintext highlighter-rouge">add</code> becomes:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">add</span><span class="p">(</span><span class="n">_</span> <span class="nv">other</span><span class="p">:</span> <span class="kt">Fraction</span><span class="p">,</span> <span class="nv">reducing</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">normalize</span><span class="p">()</span>
        <span class="k">let</span> <span class="nv">normalizedOther</span> <span class="o">=</span> <span class="n">other</span><span class="o">.</span><span class="nf">normalized</span><span class="p">()</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">denominator</span> <span class="o">==</span> <span class="n">normalizedOther</span><span class="o">.</span><span class="n">denominator</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">numerator</span> <span class="o">+=</span> <span class="n">normalizedOther</span><span class="o">.</span><span class="n">numerator</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">numerator</span> <span class="o">=</span> <span class="n">numerator</span> <span class="o">*</span> <span class="n">normalizedOther</span><span class="o">.</span><span class="n">denominator</span> <span class="o">+</span> <span class="n">normalizedOther</span><span class="o">.</span><span class="n">numerator</span> <span class="o">*</span> <span class="n">denominator</span>
            <span class="n">denominator</span> <span class="o">=</span> <span class="n">denominator</span> <span class="o">*</span> <span class="n">normalizedOther</span><span class="o">.</span><span class="n">denominator</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span> <span class="n">reducing</span> <span class="p">)</span> <span class="p">{</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">reduce</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>And that’s it. We now have a basic implementation of a type that allows us to easily perform calculations on fractions, without loss of precision.</p>

<p>You can find the full source code, including implementations for the remainder of the arithmetic operators, and unit tests, <a href="https://github.com/SintraWorks/Fraction/">here</a>.</p>]]></content><author><name></name></author><category term="fractions" /><category term="math" /><category term="swift" /><category term="fractions" /><category term="math" /><category term="swift" /><summary type="html"><![CDATA[A Swift type to operate on Fractions without loss of precision.]]></summary></entry><entry><title type="html">Observers as an alternative to delegates</title><link href="https://sintraworks.github.io/swift/2019/07/07/observables-1.html" rel="alternate" type="text/html" title="Observers as an alternative to delegates" /><published>2019-07-07T00:00:00+00:00</published><updated>2019-07-07T00:00:00+00:00</updated><id>https://sintraworks.github.io/swift/2019/07/07/observables-1</id><content type="html" xml:base="https://sintraworks.github.io/swift/2019/07/07/observables-1.html"><![CDATA[<p>Recently I started studying Ray Wenderlich’s tutorial book RxSwift. Early on, the book suggests using Rx as an alternative to delegates and their associated protocols. I though, great, but, for the task at hand, that’s a rather heavy handed solution. (Yes, I know it is just an example designed to teach me the basics.) So I tried to imagine how I could achieve something similar to the functionality they built for their initial examples, without the overhead of importing a big library like RxSwift.</p>

<p>It quickly occured to me that the concept of a <em>light-weight</em> observable was a good avenue to explore. So I jotted down the following:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">Observable</span> <span class="p">{</span>
    <span class="kd">associatedtype</span> <span class="kt">Observation</span>

    <span class="k">var</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">?</span> <span class="p">{</span> <span class="k">get</span> <span class="k">set</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Objects that want to be observable should conform to the <code class="language-plaintext highlighter-rouge">Observable</code> protocol. The thing they offer for observation is defined by typealiasing its associated type <code class="language-plaintext highlighter-rouge">Observation</code>. They must also implement a property holding an observer which will be triggered whenever the observable wants to convey an update.</p>

<p>Ideally, I would want an <code class="language-plaintext highlighter-rouge">observer</code> to be any object conforming to an <code class="language-plaintext highlighter-rouge">Observer</code> protocol. Like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">Observer</span> <span class="p">{</span>
    <span class="kd">associatedtype</span> <span class="kt">Observation</span>

    <span class="kd">func</span> <span class="nf">observe</span><span class="p">(</span><span class="n">_</span> <span class="nv">thing</span><span class="p">:</span> <span class="kt">Observation</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// …handle thing…</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Just like in the delegate pattern, the observer—the object interested in observing an <code class="language-plaintext highlighter-rouge">Observable</code>—would pass itself as the <code class="language-plaintext highlighter-rouge">observer</code>, defining a function named <code class="language-plaintext highlighter-rouge">observe</code>, to be called by the <code class="language-plaintext highlighter-rouge">Observer</code>, and which processes the <code class="language-plaintext highlighter-rouge">thing</code> it receives.</p>

<p>But you can’t do that in Swift. Protocols with associated types can only be used as generic constraints, not as first class types. If you try to implement this, the compiler will very kindly present you with an error:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">MyClass</span><span class="p">:</span> <span class="kt">Observer</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">Observation</span> <span class="o">=</span> <span class="kt">Thing</span>

    <span class="kd">func</span> <span class="nf">observe</span><span class="p">(</span><span class="n">_</span> <span class="nv">thing</span><span class="p">:</span> <span class="kt">Observation</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// …handle thing…</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// error: Protocol 'Observer' can only be used as a generic constraint</span>
<span class="c1">// because it has Self or associated type requirements.</span>
</code></pre></div></div>

<p>So, instead, we use an intermediate object with a generic type:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">handler</span><span class="p">:</span> <span class="p">(</span><span class="kt">Observation</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span>

    <span class="kd">func</span> <span class="nf">observe</span><span class="p">(</span><span class="n">_</span> <span class="nv">thing</span><span class="p">:</span> <span class="kt">Observation</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">handler</span><span class="p">(</span><span class="n">thing</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Observable</code> object defines an <code class="language-plaintext highlighter-rouge">observer</code> property, to be called into action when something is to be oserved. The observing object creates an<code class="language-plaintext highlighter-rouge">Observer</code> and passes it to the <code class="language-plaintext highlighter-rouge">Observable</code>. The <code class="language-plaintext highlighter-rouge">Observer</code> holds a handler, defined by the object that is requesting the observation. To be able to write the handler we need to know the type of <code class="language-plaintext highlighter-rouge">Observation</code>, the actual thing we are observing. To that end we typealias <code class="language-plaintext highlighter-rouge">Observation</code> in the Observable object to whatever we offer for observation:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ObservableViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="kt">Observable</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">Observation</span> <span class="o">=</span> <span class="kt">Int</span>

    <span class="k">var</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">?</span>
	<span class="err">…</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this case the observing object has to provide an observer whose generic type is an <code class="language-plaintext highlighter-rouge">Int</code>. You could also <code class="language-plaintext highlighter-rouge">typealias Observation = Void</code>, and provide an observer whose handler doesn’t take any arguments.</p>

<p>But here is a very cool thing you can do: rather than define some delegate protocol, which, in (pure) Swift, can’t have optional functions, we can use an <code class="language-plaintext highlighter-rouge">enum</code> that contains all the actions we want to offer for observation, and, since it is an <code class="language-plaintext highlighter-rouge">enum</code>, on the observing side, we do not need to handle all cases. Only the ones we are interested in. Here’s a trivial example:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ObservableViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="kt">Observable</span> <span class="p">{</span>
    <span class="kd">enum</span> <span class="kt">Action</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">button1Tapped</span>
        <span class="k">case</span> <span class="nf">button2Tapped</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">typealias</span> <span class="kt">Observation</span> <span class="o">=</span> <span class="kt">Action</span>

    <span class="k">var</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">?</span>
	<span class="err">…</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This has a downside, of course, since the observable might want to require certain actions to be implemented. In that case, we could choose to fall back to using a delegate protocol. But this construct can be a handy tool in our toolbox, for when we want to define a set of optional functions. What I also like is that it concentrates the API in one clearly defined place, both in the Observer and the Observee. And, compared to just using closures for each option (another alternative to using the delegate pattern with a delegate protocol), there’s the advantage of labeled parameters, which the closures don’t allow. (The full source code, linked to below, includes examples of both types of implementation so that you can easily compare the resulting code.)</p>

<p>When the observable has something to convey to the observer, it will call its handler: <code class="language-plaintext highlighter-rouge">observer?.observe(.button1Tapped)</code>, or <code class="language-plaintext highlighter-rouge">observer?.observe(.button2Tapped(message: "Remote action on button 2"))</code>.</p>

<p>How does that look on the observing side?</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ObservingViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">history</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">]()</span>
    
    <span class="kd">@IBAction</span> <span class="kd">func</span> <span class="nf">showObservableViewController</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">vc</span> <span class="o">=</span> <span class="nf">setupObservableViewController</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        <span class="k">self</span><span class="o">.</span><span class="n">navigationController</span><span class="p">?</span><span class="o">.</span><span class="nf">pushViewController</span><span class="p">(</span><span class="n">vc</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">setupObservableViewController</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">UIViewController</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">vc</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">storyboard</span><span class="p">?</span><span class="o">.</span><span class="nf">instantiateViewController</span><span class="p">(</span><span class="nv">withIdentifier</span><span class="p">:</span> <span class="s">"ObservableViewController"</span><span class="p">)</span> <span class="k">as?</span> <span class="kt">ObservableViewController</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span>

        <span class="n">vc</span><span class="o">.</span><span class="n">observer</span> <span class="o">=</span> <span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">ObservableViewController</span><span class="o">.</span><span class="kt">Action</span><span class="o">&gt;</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="p">(</span><span class="n">action</span><span class="p">)</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="o">=</span> <span class="k">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>

            <span class="k">switch</span> <span class="n">action</span> <span class="p">{</span>
            <span class="k">case</span> <span class="o">.</span><span class="nv">button1Tapped</span><span class="p">:</span>
                <span class="k">self</span><span class="o">.</span><span class="n">history</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="k">self</span><span class="o">.</span><span class="n">history</span><span class="o">.</span><span class="n">count</span> <span class="o">+</span> <span class="mi">1</span><span class="se">)</span><span class="s"> Remote action on button 1"</span><span class="p">)</span>
            <span class="k">case</span> <span class="o">.</span><span class="nf">button2Tapped</span><span class="p">(</span><span class="k">let</span> <span class="nv">message</span><span class="p">):</span>
                <span class="k">self</span><span class="o">.</span><span class="n">history</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="k">self</span><span class="o">.</span><span class="n">history</span><span class="o">.</span><span class="n">count</span> <span class="o">+</span> <span class="mi">1</span><span class="se">)</span><span class="s"> "</span> <span class="o">+</span> <span class="n">message</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="n">vc</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>When we set up the observable view controller, we provide it an <code class="language-plaintext highlighter-rouge">Observer</code>. The <code class="language-plaintext highlighter-rouge">Observer</code> class has an initializer that allows us to include a handler that receives the <code class="language-plaintext highlighter-rouge">Observation</code> (<code class="language-plaintext highlighter-rouge">action</code>). Our handler switches over it, handling only the cases it is interested in. Finally, we push the observable view controller onto the navigation stack, and, whenever it calls our handler, our code processes the changes.</p>

<p>This pattern can also be used to turn UIControls into lightweight reactive objects. You can read all about that in the <a href="https://sintraworks.github.io/swift/2019/07/07/observables-2.html">next post</a>.</p>

<p>You can find the full source code <a href="https://github.com/SintraWorks/Observations">here</a> (including a <code class="language-plaintext highlighter-rouge">MultiObservable</code> protocol, for observables that can service multiple observers, and a couple of observable <code class="language-plaintext highlighter-rouge">UIControl</code> derivatives).</p>]]></content><author><name></name></author><category term="swift" /><category term="swift" /><category term="reactive" /><category term="macOS" /><category term="iOS" /><summary type="html"><![CDATA[How to replace delegates with light-weight observables.]]></summary></entry><entry><title type="html">Observable controls</title><link href="https://sintraworks.github.io/swift/2019/07/07/observables-2.html" rel="alternate" type="text/html" title="Observable controls" /><published>2019-07-07T00:00:00+00:00</published><updated>2019-07-07T00:00:00+00:00</updated><id>https://sintraworks.github.io/swift/2019/07/07/observables-2</id><content type="html" xml:base="https://sintraworks.github.io/swift/2019/07/07/observables-2.html"><![CDATA[<p>Continuing on our <a href="https://sintraworks.github.io/swift/2019/07/07/observables-1.html">previous post</a>, let’s create some observable controls, and a view controller that observes another view controller.</p>

<p>Let’s say we want a button that we can observe. Rather than by setting a target and a selector and a control event, as we do traditionally, like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">control</span><span class="o">.</span><span class="nf">addTarget</span><span class="p">(</span><span class="n">bridge</span><span class="p">,</span> <span class="nv">action</span><span class="p">:</span> <span class="n">selector</span><span class="p">,</span> <span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">touchUpInside</span><span class="p">)</span>
</code></pre></div></div>

<p>…where we have to define a method whose selector we pass to the control, we want to be able to add observers like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">button1</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">touchUpInside</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">button1Pressed</span><span class="p">()</span>
        <span class="p">})</span>
</code></pre></div></div>

<p>We pass only the control event(s) we are interested in and the corresponding handler to be called when an event occurs.</p>

<p>How do we go about designing such a control?</p>

<p>Well, there is an issue that we need to get out of the way first: Since we want to observe a UIControl, and eventually have to add a target and provide a selector, we need to have functions that can serve as selectors. Meaning, they have to be visible from Objective-C. Since objects sporting generics aren’t visible from ObjC, we need a bridge that provides the crossing from Swift to ObjC. This bridge will provide for all the selectors that are meaningful to a UIControl, and it will hold a reference to the actual control so that it can pass actions through to it.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@objc</span>
<span class="kd">final</span> <span class="kd">private</span> <span class="kd">class</span> <span class="kt">ObjCSelectorBridge</span><span class="p">:</span> <span class="kt">NSObject</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">control</span><span class="p">:</span> <span class="kt">UIControl</span>

    <span class="kd">private</span> <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">eventSelectors</span><span class="p">:</span> <span class="p">[</span><span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="kt">RawValue</span><span class="p">:</span> <span class="kt">Selector</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">touchDown</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchDown</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">touchDownRepeat</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchDownRepeat</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">touchDragInside</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchDragInside</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">touchDragOutside</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchDragOutside</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">touchDragEnter</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchDragEnter</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">touchDragExit</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchDragExit</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">touchUpInside</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchUpInside</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">touchUpOutside</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchUpOutside</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">touchCancel</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchCancel</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">valueChanged</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">valueChanged</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">primaryActionTriggered</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">primaryActionTriggered</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">editingDidBegin</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">editingDidBegin</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">editingChanged</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">editingChanged</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">editingDidEnd</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">editingDidEnd</span><span class="kd">)</span><span class="p">,</span>
        <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="o">.</span><span class="n">editingDidEndOnExit</span><span class="o">.</span><span class="nv">rawValue</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">editingDidEndOnExit</span><span class="kd">)</span>
    <span class="p">]</span>

    <span class="kd">required</span> <span class="nf">init</span><span class="p">(</span><span class="n">with</span> <span class="nv">control</span><span class="p">:</span> <span class="kt">UIControl</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">control</span> <span class="o">=</span> <span class="n">control</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">selectors</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[(</span><span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">,</span> <span class="kt">Selector</span><span class="p">)]</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">selectors</span> <span class="o">=</span> <span class="p">[(</span><span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">,</span> <span class="kt">Selector</span><span class="p">)]()</span>
        <span class="k">for</span> <span class="n">event</span> <span class="k">in</span> <span class="n">controlEvents</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nv">selector</span> <span class="o">=</span> <span class="n">eventSelectors</span><span class="p">[</span><span class="n">event</span><span class="o">.</span><span class="n">rawValue</span><span class="p">]</span> <span class="p">{</span>
                <span class="n">selectors</span><span class="o">.</span><span class="nf">append</span><span class="p">((</span><span class="n">event</span><span class="p">,</span> <span class="n">selector</span><span class="p">))</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="n">selectors</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We initialize the bridge passing in the relevant UIControl instance, and the bridge knows how to map between control events and selectors. “But, where are the selectors?” you say? Well, we put them in an extension on the bridge:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">ObjCSelectorBridge</span> <span class="p">{</span>
    <span class="kd">@objc</span> <span class="kd">fileprivate</span> <span class="kd">func</span> <span class="nf">touchDown</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">control</span><span class="o">.</span><span class="nf">sendActions</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">touchDown</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">@objc</span> <span class="kd">fileprivate</span> <span class="kd">func</span> <span class="nf">touchDownRepeat</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">control</span><span class="o">.</span><span class="nf">sendActions</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">touchDownRepeat</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="err">…</span><span class="n">etc</span><span class="err">…</span>

    <span class="kd">@objc</span> <span class="kd">fileprivate</span> <span class="kd">func</span> <span class="nf">editingDidEndOnExit</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">control</span><span class="o">.</span><span class="nf">sendActions</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">editingDidEndOnExit</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>These are the reason we need the bridge. They have to be visible from Objective-C, so that the UIControl can call them.</p>

<p>You may have noticed that we are actually iterating over <code class="language-plaintext highlighter-rouge">controlEvents</code>. This is an <code class="language-plaintext highlighter-rouge">Optionset</code> (of type <code class="language-plaintext highlighter-rouge">UIControl.Event</code>). How’s that possible? <code class="language-plaintext highlighter-rouge">Optionset</code>s are not <code class="language-plaintext highlighter-rouge">Collection</code>s. Well, there’s a trick for that, but its implementation is not relevant to the subject at hand. You can check it out in the full implementation, available in <a href="https://github.com/SintraWorks/Observations">the repo</a>.</p>

<p>Now that we have the bridge in place, we can define a class that will observe a UIControl:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">internal</span> <span class="kd">class</span> <span class="kt">ControlObserver</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">NSObject</span> <span class="p">{</span>
    <span class="kd">internal</span> <span class="kd">class</span> <span class="kt">EventObserver</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span>
        <span class="k">var</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span>

        <span class="nf">init</span><span class="p">(</span><span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">,</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">self</span><span class="o">.</span><span class="n">observer</span> <span class="o">=</span> <span class="n">observer</span>
            <span class="k">self</span><span class="o">.</span><span class="n">controlEvents</span> <span class="o">=</span> <span class="n">controlEvents</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">bridge</span><span class="p">:</span> <span class="kt">ObjCSelectorBridge</span>
    <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">control</span><span class="p">:</span> <span class="kt">UIControl</span><span class="p">?</span>
    <span class="kd">internal</span> <span class="k">var</span> <span class="nv">eventObservers</span> <span class="o">=</span> <span class="p">[</span><span class="kt">EventObserver</span><span class="p">]()</span>

    <span class="kd">required</span> <span class="nf">init</span><span class="p">(</span><span class="n">with</span> <span class="nv">control</span><span class="p">:</span> <span class="kt">UIControl</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">control</span> <span class="o">=</span> <span class="n">control</span>
        <span class="k">self</span><span class="o">.</span><span class="n">bridge</span> <span class="o">=</span> <span class="kt">ObjCSelectorBridge</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">control</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">ControlObserver</code>s have an associated generic type, so that we can decide what they actually observe. We use a nested helper class that holds information about the observer and the events it responds to. It sets up an empty array of <code class="language-plaintext highlighter-rouge">EventObserver</code>s, and creates and holds on to a bridge object upon initialization, where it also stores the control it will be observing.</p>

<p>In an extension on <code class="language-plaintext highlighter-rouge">ControlObserver</code> we put the utility functions for managing observers. We’ll list the two most important ones:</p>

<p>When adding an observer we request the relevant selectors for the passed in control events. Then we loop through those events and for each add an action (selector) to the control. Finally we create an <code class="language-plaintext highlighter-rouge">EventObserver</code> that stores the handler and the applicable control events:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">internal</span> <span class="kd">func</span> <span class="nf">addObserver</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Observation</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">control</span> <span class="o">=</span> <span class="n">control</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        <span class="k">let</span> <span class="nv">eventsAndSelectors</span> <span class="o">=</span> <span class="n">bridge</span><span class="o">.</span><span class="nf">selectors</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">controlEvents</span><span class="p">)</span>

        <span class="k">for</span> <span class="p">(</span><span class="n">controlEvent</span><span class="p">,</span> <span class="n">selector</span><span class="p">)</span> <span class="k">in</span> <span class="n">eventsAndSelectors</span> <span class="p">{</span>
            <span class="n">control</span><span class="o">.</span><span class="nf">addTarget</span><span class="p">(</span><span class="n">bridge</span><span class="p">,</span> <span class="nv">action</span><span class="p">:</span> <span class="n">selector</span><span class="p">,</span> <span class="nv">for</span><span class="p">:</span> <span class="n">controlEvent</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="k">let</span> <span class="nv">eventObserver</span> <span class="o">=</span> <span class="kt">EventObserver</span><span class="p">(</span><span class="nv">observer</span><span class="p">:</span> <span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">handler</span><span class="p">:</span> <span class="n">handler</span><span class="p">),</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="n">controlEvents</span><span class="p">)</span>
        <span class="n">eventObservers</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">eventObserver</span><span class="p">)</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>We allow removing all observers for a specific control event, or set of control events:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">internal</span> <span class="kd">func</span> <span class="nf">removeObservers</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">controlEvents</span> <span class="o">==</span> <span class="o">.</span><span class="n">allEvents</span> <span class="p">{</span>
            <span class="n">eventObservers</span><span class="o">.</span><span class="nf">removeAll</span><span class="p">()</span>
            <span class="k">return</span>
        <span class="p">}</span>

        <span class="k">for</span> <span class="n">event</span> <span class="k">in</span> <span class="n">controlEvents</span> <span class="p">{</span>
            <span class="c1">// Remove all observers whose controlEvents match exactly</span>
            <span class="n">eventObservers</span><span class="o">.</span><span class="nf">removeAll</span><span class="p">(</span><span class="nv">where</span><span class="p">:</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">controlEvents</span><span class="o">.</span><span class="n">rawValue</span> <span class="o">==</span> <span class="n">event</span><span class="o">.</span><span class="n">rawValue</span> <span class="p">})</span>
            <span class="c1">// For those that have more events, and can't be deleted yet, we remove the specific event.</span>
            <span class="k">for</span> <span class="n">eventObserver</span> <span class="k">in</span> <span class="n">eventObservers</span> <span class="p">{</span>
                <span class="k">if</span> <span class="n">eventObserver</span><span class="o">.</span><span class="n">controlEvents</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">eventObserver</span><span class="o">.</span><span class="n">controlEvents</span><span class="o">.</span><span class="nf">remove</span><span class="p">(</span><span class="n">event</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="c1">// Finally, we remove the action for the passed in set of control events.</span>
        <span class="n">control</span><span class="p">?</span><span class="o">.</span><span class="nf">removeTarget</span><span class="p">(</span><span class="n">bridge</span><span class="p">,</span> <span class="nv">action</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">for</span><span class="p">:</span> <span class="n">controlEvents</span><span class="p">)</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>We also allow retrieving all observers, querying for observers for specific control events, or querying the registered actions. The implementations of these can be found in the <a href="https://github.com/SintraWorks/Observations">full source code</a>.</p>

<p>So, to take stock: we now have a bridge to the Objective-C world, and we have an object that can observe a control. What we need next is a control that can be observed. For that, we create a protocol called—what else?—<code class="language-plaintext highlighter-rouge">ObservableControl</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">ObservableControl</span> <span class="p">{</span>
    <span class="kd">associatedtype</span> <span class="kt">Observation</span>

    <span class="k">var</span> <span class="nv">controlObserver</span><span class="p">:</span> <span class="kt">ControlObserver</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">addObserver</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Observation</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span>
    <span class="kd">func</span> <span class="nf">removeObservers</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span>

    <span class="kd">func</span> <span class="nf">sendActions</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span>

    <span class="kd">func</span> <span class="nf">actions</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">String</span><span class="p">]?</span>
    <span class="kd">func</span> <span class="nf">observers</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">]</span>
    <span class="k">var</span> <span class="nv">observers</span><span class="p">:</span> <span class="p">[</span><span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">]</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We need a <code class="language-plaintext highlighter-rouge">ControlObserver</code>, of course, to do the actual observing, and we define an API for adding, removing and monitoring observers, and also for the actual interactions.</p>

<p>We provide default implementations for the bulk of the requirements, to avoid having to duplicate code all over the place:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">ObservableControl</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">addObserver</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Observation</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">controlObserver</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">controlEvents</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="n">handler</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">removeObservers</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">controlObserver</span><span class="o">.</span><span class="nf">removeObservers</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">controlEvents</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">observers</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">]</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">controlObserver</span><span class="o">.</span><span class="nf">observers</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">controlEvents</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">observers</span><span class="p">:</span> <span class="p">[</span><span class="kt">Observer</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">]</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">controlObserver</span><span class="o">.</span><span class="n">observers</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">actions</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvent</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">String</span><span class="p">]?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">controlObserver</span><span class="o">.</span><span class="nf">actions</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">controlEvent</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>These simply provide a pass-through to the equivalent interface calls of the <code class="language-plaintext highlighter-rouge">ControlObserver</code>.</p>

<p>Now we are finally ready to to implement a concrete observable control. Here’s a button:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">ObservableButton</span><span class="p">:</span> <span class="kt">UIButton</span><span class="p">,</span> <span class="kt">ObservableControl</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">Observation</span> <span class="o">=</span> <span class="kt">Void</span>

    <span class="kd">lazy</span> <span class="kd">internal</span> <span class="k">var</span> <span class="nv">controlObserver</span> <span class="o">=</span> <span class="kt">ControlObserver</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span>

    <span class="k">override</span> <span class="kd">func</span> <span class="nf">sendActions</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">event</span> <span class="k">in</span> <span class="n">controlEvents</span> <span class="p">{</span>
            <span class="k">for</span> <span class="n">eventObserver</span> <span class="k">in</span> <span class="n">controlObserver</span><span class="o">.</span><span class="n">eventObservers</span> <span class="p">{</span>
                <span class="k">guard</span> <span class="n">eventObserver</span><span class="o">.</span><span class="n">controlEvents</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">continue</span> <span class="p">}</span>
                <span class="n">eventObserver</span><span class="o">.</span><span class="n">observer</span><span class="o">.</span><span class="nf">observe</span><span class="p">(())</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So, it’s a proper UIButton, but one that conforms to <code class="language-plaintext highlighter-rouge">ObservableControl</code>. As you can see, there’s not much left to do here:</p>
<ul>
  <li>We define the type of action to observe: in this case <code class="language-plaintext highlighter-rouge">Void</code> since all we are interested in is that the observer is called (for the specific control event it was registered with). There is no value that we are interested in.</li>
  <li>We create and hold on to a control observer, generic on the type we just defined.</li>
  <li>We pass triggered actions through to any relevant observers that are registered with us.</li>
</ul>

<p>How about a slider?</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">ObservableSlider</span><span class="p">:</span> <span class="kt">UISlider</span><span class="p">,</span> <span class="kt">ObservableControl</span> <span class="p">{</span>
    <span class="kd">enum</span> <span class="kt">Action</span> <span class="p">{</span>
        <span class="k">case</span> <span class="nf">isTrackingTouch</span><span class="p">(</span><span class="n">_</span> <span class="nv">flag</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
        <span class="k">case</span> <span class="nf">valueChanged</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Float</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">typealias</span> <span class="kt">Observation</span> <span class="o">=</span> <span class="kt">Action</span>

    <span class="kd">lazy</span> <span class="k">var</span> <span class="nv">controlObserver</span> <span class="o">=</span> <span class="kt">ControlObserver</span><span class="o">&lt;</span><span class="kt">Observation</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span>

    <span class="k">override</span> <span class="kd">func</span> <span class="nf">sendActions</span><span class="p">(</span><span class="k">for</span> <span class="nv">controlEvents</span><span class="p">:</span> <span class="kt">UIControl</span><span class="o">.</span><span class="kt">Event</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">event</span> <span class="k">in</span> <span class="n">controlEvents</span> <span class="p">{</span>
            <span class="k">for</span> <span class="n">eventObserver</span> <span class="k">in</span> <span class="n">controlObserver</span><span class="o">.</span><span class="n">eventObservers</span> <span class="p">{</span>
                <span class="k">guard</span> <span class="n">eventObserver</span><span class="o">.</span><span class="n">controlEvents</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
                <span class="k">switch</span> <span class="n">controlEvents</span> <span class="p">{</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">touchDown</span><span class="p">:</span>
                    <span class="n">eventObserver</span><span class="o">.</span><span class="n">observer</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span><span class="o">.</span><span class="nf">isTrackingTouch</span><span class="p">(</span><span class="kc">true</span><span class="p">))</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">valueChanged</span><span class="p">:</span>
                    <span class="n">eventObserver</span><span class="o">.</span><span class="n">observer</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span><span class="o">.</span><span class="nf">valueChanged</span><span class="p">(</span><span class="n">value</span><span class="p">))</span>
                <span class="k">case</span> <span class="o">.</span><span class="n">touchCancel</span><span class="p">,</span> <span class="o">.</span><span class="n">touchUpInside</span><span class="p">,</span> <span class="o">.</span><span class="n">touchUpOutside</span><span class="p">,</span> <span class="o">.</span><span class="n">touchDragOutside</span><span class="p">,</span> <span class="o">.</span><span class="nv">touchDragExit</span><span class="p">:</span>
                    <span class="n">eventObserver</span><span class="o">.</span><span class="n">observer</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span><span class="o">.</span><span class="nf">isTrackingTouch</span><span class="p">(</span><span class="kc">false</span><span class="p">))</span>
                <span class="k">default</span><span class="p">:</span>
                    <span class="k">break</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This one is slightly more interesting, because now we <em>are</em> interested in a value: we want to know when the value has changed, and, in this particular implementation, we also want to know when the slider is tracking a touch. For this we create an enum that provides the state changes we are interested in: <code class="language-plaintext highlighter-rouge">isTrackingTouch</code> and <code class="language-plaintext highlighter-rouge">valueChanged</code>. These cases each have an associated value: <code class="language-plaintext highlighter-rouge">isTrackingTouch</code> has a <code class="language-plaintext highlighter-rouge">flag</code> indicating whether tracking has started or ended, and <code class="language-plaintext highlighter-rouge">valueChanged</code> has the current value of the slider.</p>

<p>So, now that we have all the pieces in place, how does this look in actual use? Well, to observe a button:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">MyViewController</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">button</span> <span class="o">=</span> <span class="kt">ObservableButton</span><span class="p">(</span><span class="nv">type</span><span class="p">:</span> <span class="o">.</span><span class="n">custom</span><span class="p">)</span>
    
    <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span>
        <span class="nf">setupButtonObservers</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">setupButtonObservers</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">button</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">touchDown</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="nf">enterPanicMode</span><span class="p">()</span>
        <span class="p">})</span>

        <span class="n">button</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">touchUpInside</span><span class="p">,</span> <span class="nv">handler</span><span class="p">:</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="nf">exitPanicMode</span><span class="p">()</span>
        <span class="p">})</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To observe a slider:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">MyViewController</span><span class="p">,</span> <span class="kt">MultiObservable</span> <span class="p">{</span>
    <span class="kd">enum</span> <span class="kt">Action</span> <span class="p">{</span>
        <span class="err">…</span>
        <span class="k">case</span> <span class="nf">sliderValueChanged</span><span class="p">(</span><span class="nv">newValue</span><span class="p">:</span> <span class="kt">Float</span><span class="p">)</span>
        <span class="k">case</span> <span class="nf">sliderIsTrackingTouch</span><span class="p">(</span><span class="n">_</span> <span class="nv">flag</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
        <span class="err">…</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">progressSlider</span> <span class="o">=</span> <span class="kt">ObservableSlider</span><span class="p">()</span>
    
    <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span>
        <span class="nf">setupSliderObserver</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">setupSliderObserver</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">progressSlider</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="p">[</span>
            <span class="o">.</span><span class="n">valueChanged</span><span class="p">,</span> <span class="o">.</span><span class="n">touchDown</span><span class="p">,</span> <span class="o">.</span><span class="n">touchUpInside</span><span class="p">,</span>
            <span class="o">.</span><span class="n">touchUpOutside</span><span class="p">,</span> <span class="o">.</span><span class="n">touchCancel</span><span class="p">,</span> <span class="o">.</span><span class="n">touchDragExit</span>
        <span class="p">])</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="p">(</span><span class="n">action</span><span class="p">)</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="o">=</span> <span class="k">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>

            <span class="k">switch</span> <span class="n">action</span> <span class="p">{</span>
            <span class="k">case</span> <span class="o">.</span><span class="nf">valueChanged</span><span class="p">(</span><span class="k">let</span> <span class="nv">newValue</span><span class="p">):</span>
                <span class="k">for</span> <span class="n">observer</span> <span class="k">in</span> <span class="k">self</span><span class="o">.</span><span class="n">observers</span> <span class="p">{</span>
                    <span class="n">observer</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span><span class="o">.</span><span class="nf">sliderValueChanged</span><span class="p">(</span><span class="nv">newValue</span><span class="p">:</span> <span class="n">newValue</span><span class="p">))</span>
                <span class="p">}</span>
            <span class="k">case</span> <span class="o">.</span><span class="nf">isTrackingTouch</span><span class="p">(</span><span class="k">let</span> <span class="nv">isTracking</span><span class="p">):</span>
                <span class="k">for</span> <span class="n">observer</span> <span class="k">in</span> <span class="k">self</span><span class="o">.</span><span class="n">observers</span> <span class="p">{</span>
                    <span class="n">observer</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span><span class="o">.</span><span class="nf">interactiveStateChanged</span><span class="p">(</span><span class="nv">isActive</span><span class="p">:</span> <span class="n">isTracking</span><span class="p">))</span>
                    <span class="n">observer</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span><span class="o">.</span><span class="nf">sliderIsTrackingTouch</span><span class="p">(</span><span class="n">isTracking</span><span class="p">))</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Notice, in this last example, that the view controller itself is also observable. In fact, it conforms to <code class="language-plaintext highlighter-rouge">MultiObservable</code>, meaning it can have multiple observers. When a slider is interacted with, the controller informs all of its observers of the slider’s new value as its thumb is dragged, and also conveys some other state changes.</p>

<p>I started developing this technique some time before WWDC 2019. Meanwhile, WWDC came and passed and SwiftUI will soon be upon us, with its own variation of attaching handlers to controls. But the technique shown here is available right now, and compatible with iOS 11 and iOS 12, whereas SwiftUI is available only from iOS 13 onwards.</p>

<p>You can find the full source code <a href="https://github.com/SintraWorks/Observations">here</a>.</p>]]></content><author><name></name></author><category term="swift" /><category term="swift" /><category term="reactive" /><category term="macOS" /><category term="iOS" /><summary type="html"><![CDATA[How to turn UIControls into light-weight reactive objects.]]></summary></entry><entry><title type="html">A Scrolling Stackview</title><link href="https://sintraworks.github.io/swift/uikit/2018/12/24/scrolling-stackview.html" rel="alternate" type="text/html" title="A Scrolling Stackview" /><published>2018-12-24T00:00:00+00:00</published><updated>2018-12-24T00:00:00+00:00</updated><id>https://sintraworks.github.io/swift/uikit/2018/12/24/scrolling-stackview</id><content type="html" xml:base="https://sintraworks.github.io/swift/uikit/2018/12/24/scrolling-stackview.html"><![CDATA[<p>Some months ago I read <a href="https://blog.alltheflow.com/scrollable-uistackview/" title="Scrollable UIStackView">this article</a> by Agnes Vasarhelyi. It’s about–guess what–scrollable UIStackViews. More precisely, it’s about how to correctly set up a UIStackView within a UIScrollView, using autolayout. Not long after that, I needed extactly that: a scrolling stack view for a screen I was developing at work. I decided to create something simple, yet convenient and reusable. I didn’t want to create a fancy view controller with all manner of bells and whistles.  Just a simple view, that acts as scrolling stack view. Also, I did not want to have to write something like <code class="language-plaintext highlighter-rouge">scrollView.stackView.axis = .vertical</code>, but rather <code class="language-plaintext highlighter-rouge">stackView.axis = .vertical</code>.</p>

<p>So, we start with a scroll view. Upon initialization we embed the stack view within it. There are a couple of initializers we must implement. Additionally, we want to be able to set the scrolling axis during initialization, so we add an initializer for that. We also add a convenience initializer to facilitate creating the stack view with an array of arranged subviews:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ScrollingStackView</span><span class="p">:</span> <span class="kt">UIScrollView</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">stackView</span> <span class="o">=</span> <span class="kt">UIStackView</span><span class="p">()</span>

    <span class="kd">required</span> <span class="nf">init</span><span class="p">?(</span><span class="n">coder</span> <span class="nv">aDecoder</span><span class="p">:</span> <span class="kt">NSCoder</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">coder</span><span class="p">:</span> <span class="n">aDecoder</span><span class="p">)</span>
        <span class="nf">commonInit</span><span class="p">(</span><span class="nv">axis</span><span class="p">:</span> <span class="o">.</span><span class="n">vertical</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">frame</span><span class="p">:</span> <span class="kt">CGRect</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span>
        <span class="nf">commonInit</span><span class="p">(</span><span class="nv">axis</span><span class="p">:</span> <span class="o">.</span><span class="n">vertical</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="nf">init</span><span class="p">(</span><span class="nv">axis</span><span class="p">:</span> <span class="kt">UILayoutConstraintAxis</span> <span class="o">=</span> <span class="o">.</span><span class="n">vertical</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">frame</span><span class="p">:</span> <span class="kt">CGRect</span><span class="o">.</span><span class="n">zero</span><span class="p">)</span>
        <span class="nf">commonInit</span><span class="p">(</span><span class="nv">axis</span><span class="p">:</span> <span class="n">axis</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">convenience</span> <span class="nf">init</span><span class="p">(</span><span class="nv">arrangedSubviews</span><span class="p">:</span> <span class="p">[</span><span class="kt">UIView</span><span class="p">],</span> <span class="nv">axis</span><span class="p">:</span> <span class="kt">UILayoutConstraintAxis</span> <span class="o">=</span> <span class="o">.</span><span class="n">vertical</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">axis</span><span class="p">:</span> <span class="n">axis</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">subview</span> <span class="k">in</span> <span class="n">arrangedSubviews</span> <span class="p">{</span>
            <span class="n">stackView</span><span class="o">.</span><span class="nf">addArrangedSubview</span><span class="p">(</span><span class="n">subview</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">commonInit</span><span class="p">(</span><span class="nv">axis</span><span class="p">:</span> <span class="kt">UILayoutConstraintAxis</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">embed</span><span class="p">(</span><span class="nv">view</span><span class="p">:</span> <span class="n">stackView</span><span class="p">)</span> <span class="c1">// See bottom of article for the embed extension to UIView. </span>
        <span class="k">self</span><span class="o">.</span><span class="n">axis</span> <span class="o">=</span> <span class="n">axis</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You may have raised an eyebrow at the implementation of the <code class="language-plaintext highlighter-rouge">commonInit</code> function. We set an <code class="language-plaintext highlighter-rouge">axis</code> property directly on our scroll view subclass. But “a UIScrollView doesn’t have an axis property” I hear you say. That’s right. We define a computed property on the scroll view, that manages the axis property of the stack view:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">var</span> <span class="nv">axis</span><span class="p">:</span> <span class="kt">UILayoutConstraintAxis</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="n">axis</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span>
            <span class="n">axisConstraint</span><span class="p">?</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">false</span> <span class="c1">// deactivate existing constraint, if any</span>
            <span class="k">switch</span> <span class="n">newValue</span> <span class="k">as</span> <span class="kt">UILayoutConstraintAxis</span> <span class="p">{</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">vertical</span><span class="p">:</span>
                    <span class="n">axisConstraint</span> <span class="o">=</span> <span class="n">stackView</span><span class="o">.</span><span class="n">widthAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">widthAnchor</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">horizontal</span><span class="p">:</span>
                    <span class="n">axisConstraint</span> <span class="o">=</span> <span class="n">stackView</span><span class="o">.</span><span class="n">heightAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">heightAnchor</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="n">axisConstraint</span><span class="p">?</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span> <span class="c1">// activate new constraint</span>

            <span class="n">stackView</span><span class="o">.</span><span class="n">axis</span> <span class="o">=</span> <span class="n">newValue</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>When querying the <code class="language-plaintext highlighter-rouge">axis</code> property, we simply and conveniently return the stack view’s <code class="language-plaintext highlighter-rouge">axis</code> property. But when setting the property, we ensure the stack view is set up correctly. If you took the time to read <a href="https://blog.alltheflow.com/scrollable-uistackview/" title="Scrollable UIStackView">Agnes Vasarhelyi’s article</a>, you’ll know we need to set either a width or a height constraint on the stack view, to make the scroll view happy. We need a property to manage that constraint, since we need to disable the old constraint when switching axes. Therefore, we added a property to ScrollingStackView:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ScrollingStackView</span><span class="p">:</span> <span class="kt">UIScrollView</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">stackView</span> <span class="o">=</span> <span class="kt">UIStackView</span><span class="p">()</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">axisConstraint</span><span class="p">:</span> <span class="kt">NSLayoutConstraint</span><span class="p">?</span>
	<span class="err">…</span>
<span class="p">}</span>
</code></pre></div></div>
<p>When changing axis, we deactivate the existing axis constraint and create and activate the new one, and hold on to it for future reference. This is really all we need to correctly manage a stack view inside a scroll view.</p>

<p>But, we also want to make it convenient to work with it. We don’t want to be exposed to the fact that the stack view is encapsulated inside a scroll view (unless when there’s a need for it). That’s easily achieved by vending the stack view’s properties straight from our encapsulating view:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// MARK: - Pass-throughs to UIStackView</span>

<span class="c1">// MARK: Managing arranged subviews</span>
<span class="kd">extension</span> <span class="kt">ScrollingStackView</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">addArrangedSubview</span><span class="p">(</span><span class="n">_</span> <span class="nv">view</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stackView</span><span class="o">.</span><span class="nf">addArrangedSubview</span><span class="p">(</span><span class="n">view</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">arrangedSubviews</span><span class="p">:</span> <span class="p">[</span><span class="kt">UIView</span><span class="p">]</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="n">arrangedSubviews</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">insertArrangedSubview</span><span class="p">(</span><span class="n">_</span> <span class="nv">view</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">,</span> <span class="n">at</span> <span class="nv">stackIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stackView</span><span class="o">.</span><span class="nf">insertArrangedSubview</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span> <span class="n">stackIndex</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">removeArrangedSubview</span><span class="p">(</span><span class="n">_</span> <span class="nv">view</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stackView</span><span class="o">.</span><span class="nf">removeArrangedSubview</span><span class="p">(</span><span class="n">view</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// MARK: Configuring the layout</span>
<span class="kd">extension</span> <span class="kt">ScrollingStackView</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">alignment</span><span class="p">:</span> <span class="kt">UIStackViewAlignment</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="n">alignment</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span> <span class="n">stackView</span><span class="o">.</span><span class="n">alignment</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">axis</span><span class="p">:</span> <span class="kt">UILayoutConstraintAxis</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="n">axis</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span>
            <span class="n">axisConstraint</span><span class="p">?</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">false</span> <span class="c1">// deactivate existing constraint, if any</span>
            <span class="k">switch</span> <span class="n">newValue</span> <span class="k">as</span> <span class="kt">UILayoutConstraintAxis</span> <span class="p">{</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">vertical</span><span class="p">:</span>
                    <span class="n">axisConstraint</span> <span class="o">=</span> <span class="n">stackView</span><span class="o">.</span><span class="n">widthAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">widthAnchor</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">horizontal</span><span class="p">:</span>
                    <span class="n">axisConstraint</span> <span class="o">=</span> <span class="n">stackView</span><span class="o">.</span><span class="n">heightAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">heightAnchor</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="n">axisConstraint</span><span class="p">?</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span> <span class="c1">// activate new constraint</span>

            <span class="n">stackView</span><span class="o">.</span><span class="n">axis</span> <span class="o">=</span> <span class="n">newValue</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">isBaselineRelativeArrangement</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="n">isBaselineRelativeArrangement</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span> <span class="n">stackView</span><span class="o">.</span><span class="n">isBaselineRelativeArrangement</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">distribution</span><span class="p">:</span> <span class="kt">UIStackViewDistribution</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="n">distribution</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span> <span class="n">stackView</span><span class="o">.</span><span class="n">distribution</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">isLayoutMarginsRelativeArrangement</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="n">isLayoutMarginsRelativeArrangement</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span> <span class="n">stackView</span><span class="o">.</span><span class="n">isLayoutMarginsRelativeArrangement</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">spacing</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="n">spacing</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span> <span class="n">stackView</span><span class="o">.</span><span class="n">spacing</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// MARK: Adding space between items</span>
<span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">11.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
<span class="kd">extension</span> <span class="kt">ScrollingStackView</span> <span class="p">{</span>
        <span class="kd">func</span> <span class="nf">customSpacing</span><span class="p">(</span><span class="n">after</span> <span class="nv">arrangedSubview</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="nf">customSpacing</span><span class="p">(</span><span class="nv">after</span><span class="p">:</span> <span class="n">arrangedSubview</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">setCustomSpacing</span><span class="p">(</span><span class="n">_</span> <span class="nv">spacing</span><span class="p">:</span> <span class="kt">CGFloat</span><span class="p">,</span> <span class="n">after</span> <span class="nv">arrangedSubview</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stackView</span><span class="o">.</span><span class="nf">setCustomSpacing</span><span class="p">(</span><span class="n">spacing</span><span class="p">,</span> <span class="nv">after</span><span class="p">:</span> <span class="n">arrangedSubview</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">class</span> <span class="k">var</span> <span class="nv">spacingUseDefault</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">UIStackView</span><span class="o">.</span><span class="n">spacingUseDefault</span>
    <span class="p">}</span>

    <span class="kd">class</span> <span class="k">var</span> <span class="nv">spacingUseSystem</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">UIStackView</span><span class="o">.</span><span class="n">spacingUseSystem</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// MARK: - UIView overrides</span>
<span class="kd">extension</span> <span class="kt">ScrollingStackView</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">var</span> <span class="nv">layoutMargins</span><span class="p">:</span> <span class="kt">UIEdgeInsets</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">stackView</span><span class="o">.</span><span class="n">layoutMargins</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span> <span class="n">stackView</span><span class="o">.</span><span class="n">layoutMargins</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This passing thorugh of UIStackView’s properties is what makes our view feel like a UIStackView, even if it isn’t really. Now, all we need to do is set up a ScrollingStackview, and add a stack of subviews to it:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>

<span class="k">let</span> <span class="nv">stackView</span> <span class="o">=</span> <span class="kt">ScrollingStackView</span><span class="p">()</span>
<span class="err">…</span>
<span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span>

<span class="n">view</span><span class="o">.</span><span class="nf">embed</span><span class="p">(</span><span class="n">stackView</span><span class="p">)</span>
<span class="nf">setupStackView</span><span class="p">()</span>
<span class="p">}</span>
<span class="err">…</span>
<span class="kd">func</span> <span class="nf">setupStackView</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">stackView</span><span class="o">.</span><span class="n">spacing</span> <span class="o">=</span> <span class="mf">10.0</span>

    <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..&lt;</span><span class="mi">30</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">label</span> <span class="o">=</span> <span class="kt">UILabel</span><span class="p">()</span>
        <span class="n">label</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s">"Hello, this is label nº </span><span class="se">\(</span><span class="n">i</span><span class="se">)</span><span class="s">"</span>
        <span class="n">stackView</span><span class="o">.</span><span class="nf">addArrangedSubview</span><span class="p">(</span><span class="n">label</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And that’s it. Our ScrollingStackView will automagically scroll as needed. If you want a horizontally scrolling stackview, initialize it like this: <code class="language-plaintext highlighter-rouge">let stackView = ScrollingStackView(axis: .horizontal)</code>.</p>

<p>Full source code <a href="https://gist.github.com/SintraWorks/d6d692a09d9a340517529d139894f6c9" title="ScrollingStackView gist">in this gist</a>.</p>

<p>Lastly, here is the convenience function we used above, to embed a view within another view, with the subview completely filling the parent view:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIView</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">embed</span><span class="p">(</span><span class="n">_</span> <span class="nv">child</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">child</span><span class="o">.</span><span class="n">translatesAutoresizingMaskIntoConstraints</span> <span class="o">=</span> <span class="kc">false</span>
        <span class="nf">addSubview</span><span class="p">(</span><span class="n">child</span><span class="p">)</span>
        <span class="k">let</span> <span class="nv">constraints</span> <span class="o">=</span> <span class="p">[</span>
            <span class="n">child</span><span class="o">.</span><span class="n">leftAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">leftAnchor</span><span class="p">),</span>
            <span class="n">child</span><span class="o">.</span><span class="n">rightAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">rightAnchor</span><span class="p">),</span>
            <span class="n">child</span><span class="o">.</span><span class="n">topAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">topAnchor</span><span class="p">),</span>
            <span class="n">child</span><span class="o">.</span><span class="n">bottomAnchor</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">bottomAnchor</span><span class="p">),</span>
            <span class="p">]</span>
        <span class="kt">NSLayoutConstraint</span><span class="o">.</span><span class="nf">activate</span><span class="p">(</span><span class="n">constraints</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="swift" /><category term="uikit" /><category term="swift" /><category term="uikit" /><category term="iOS" /><category term="autolayout" /><summary type="html"><![CDATA[A drop-in UIStackView replacement that can actually scroll.]]></summary></entry><entry><title type="html">PleasantNavigationController</title><link href="https://sintraworks.github.io/swift/uikit/2018/11/01/a-better-navigation-controller.html" rel="alternate" type="text/html" title="PleasantNavigationController" /><published>2018-11-01T00:00:00+00:00</published><updated>2018-11-01T00:00:00+00:00</updated><id>https://sintraworks.github.io/swift/uikit/2018/11/01/a-better-navigation-controller</id><content type="html" xml:base="https://sintraworks.github.io/swift/uikit/2018/11/01/a-better-navigation-controller.html"><![CDATA[<p>So you’re building your app, and have a navigation controller, and sometimes you need to know when the controller is about to navigate, or has just finished navigating. Well, there’s a great built in API for that, right?</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="nv">navigationController</span><span class="p">:(</span><span class="kt">UINavigationController</span> <span class="o">*</span><span class="p">)</span><span class="n">navigationController</span> <span class="nv">willShowViewController</span><span class="p">:(</span><span class="kt">UIViewController</span> <span class="o">*</span><span class="p">)</span><span class="n">viewController</span> <span class="nv">animated</span><span class="p">:(</span><span class="kt">BOOL</span><span class="p">)</span><span class="n">animated</span><span class="p">;</span>
<span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="nv">navigationController</span><span class="p">:(</span><span class="kt">UINavigationController</span> <span class="o">*</span><span class="p">)</span><span class="n">navigationController</span> <span class="nv">didShowViewController</span><span class="p">:(</span><span class="kt">UIViewController</span> <span class="o">*</span><span class="p">)</span><span class="n">viewController</span> <span class="nv">animated</span><span class="p">:(</span><span class="kt">BOOL</span><span class="p">)</span><span class="n">animated</span><span class="p">;</span>
</code></pre></div></div>

<p>Very nice indeed. Now, your view controller is about to be shown, and you need to know whether the user got there by navigating back, e.g. by tapping the back button, or by swiping right to exit the current top controller in the stack. Ouch. You don’t know. And UINavigationcontroller won’t tell you. All it will tell you is “when [it] shows a new top view controller via a push, pop or setting of the view controller stack”. You never know the direction of the move. There are hacks to detect a back button tap, or a right-swipe and pass that along. These I find unsatisfactory. It would be better if UINavigationController told its delegate more about its actions. And, as it turns out, it is not difficult to extend UINavigationController to do exactly that.</p>

<p>Enter <code class="language-plaintext highlighter-rouge">PleasantNavigationController</code> and its <code class="language-plaintext highlighter-rouge">PleasantNavigationControllerDelegate</code> protocol.</p>

<p>We start by defining the API we want to subscribe to. We want to be told when a navigation event is about to happen, and when it has just finished, and we want to know the direction of the move:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@objc</span> <span class="kd">protocol</span> <span class="kt">PleasantNavigationControllerDelegate</span><span class="p">:</span> <span class="kt">AnyObject</span> <span class="p">{</span>
    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">willPushViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">didPushViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>

    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">willPopViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">?,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">didPopViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">?,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>

    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">willPopToViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">didPopToViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>

    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">willPopToRootViewController</span><span class="p">(</span><span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">didPopToRootViewController</span><span class="p">(</span><span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>

    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">willSetViewControllers</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewControllers</span><span class="p">:</span> <span class="p">[</span><span class="kt">UIViewController</span><span class="p">],</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
    <span class="kd">@objc</span> <span class="kd">optional</span> <span class="kd">func</span> <span class="nf">didSetViewControllers</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewControllers</span><span class="p">:</span> <span class="p">[</span><span class="kt">UIViewController</span><span class="p">],</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This provides us with all the relevant events, so that we can react to them as needed. Now we need a version of the navigation controller that implements the protocol:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">PleasantNavigationController</span><span class="p">:</span> <span class="kt">UINavigationController</span> <span class="p">{</span>
	<span class="c1">// First, some obligatory overrides</span>
    <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="n">nibName</span> <span class="nv">nibNameOrNil</span><span class="p">:</span> <span class="kt">String</span><span class="p">?,</span> <span class="n">bundle</span> <span class="nv">nibBundleOrNil</span><span class="p">:</span> <span class="kt">Bundle</span><span class="p">?)</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">nibName</span><span class="p">:</span> <span class="n">nibNameOrNil</span><span class="p">,</span> <span class="nv">bundle</span><span class="p">:</span> <span class="n">nibBundleOrNil</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">required</span> <span class="nf">init</span><span class="p">?(</span><span class="n">coder</span> <span class="nv">aDecoder</span><span class="p">:</span> <span class="kt">NSCoder</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">coder</span><span class="p">:</span> <span class="n">aDecoder</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">rootViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">rootViewController</span><span class="p">:</span> <span class="n">rootViewController</span><span class="p">)</span>
    <span class="p">}</span>
	
	<span class="c1">// Sometimes we need to be able to access the delegate with the more refined type.</span>
    <span class="k">var</span> <span class="nv">pleasantDelegate</span><span class="p">:</span> <span class="kt">PleasantNavigationControllerDelegate</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">delegate</span> <span class="k">as?</span> <span class="kt">PleasantNavigationControllerDelegate</span>
    <span class="p">}</span>

	<span class="c1">// And, finally, the functions that implement our protocol</span>
    <span class="k">override</span> <span class="kd">func</span> <span class="nf">pushViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">willPushViewController</span><span class="p">?(</span><span class="n">viewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">pushViewController</span><span class="p">(</span><span class="n">viewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
        <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">didPushViewController</span><span class="p">?(</span><span class="n">viewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="kd">func</span> <span class="nf">popViewController</span><span class="p">(</span><span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">UIViewController</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">poppedViewController</span> <span class="o">=</span> <span class="n">topViewController</span>
        <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">willPopViewController</span><span class="p">?(</span><span class="n">poppedViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
        <span class="k">defer</span> <span class="p">{</span> <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">didPopViewController</span><span class="p">?(</span><span class="n">poppedViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span> <span class="p">}</span>
        <span class="k">return</span> <span class="k">super</span><span class="o">.</span><span class="nf">popViewController</span><span class="p">(</span><span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="kd">func</span> <span class="nf">popToViewController</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">UIViewController</span><span class="p">]?</span> <span class="p">{</span>
        <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">willPopToViewController</span><span class="p">?(</span><span class="n">viewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
        <span class="k">defer</span> <span class="p">{</span> <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">didPopToViewController</span><span class="p">?(</span><span class="n">viewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span> <span class="p">}</span>
        <span class="k">return</span> <span class="k">super</span><span class="o">.</span><span class="nf">popToViewController</span><span class="p">(</span><span class="n">viewController</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="kd">func</span> <span class="nf">popToRootViewController</span><span class="p">(</span><span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">UIViewController</span><span class="p">]?</span> <span class="p">{</span>
        <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">willPopToRootViewController</span><span class="p">?(</span><span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
        <span class="k">defer</span> <span class="p">{</span> <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">didPopToRootViewController</span><span class="p">?(</span><span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span> <span class="p">}</span>
        <span class="k">return</span> <span class="k">super</span><span class="o">.</span><span class="nf">popToRootViewController</span><span class="p">(</span><span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="kd">func</span> <span class="nf">setViewControllers</span><span class="p">(</span><span class="n">_</span> <span class="nv">viewControllers</span><span class="p">:</span> <span class="p">[</span><span class="kt">UIViewController</span><span class="p">],</span> <span class="nv">animated</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">willSetViewControllers</span><span class="p">?(</span><span class="n">viewControllers</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">setViewControllers</span><span class="p">(</span><span class="n">viewControllers</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
        <span class="n">pleasantDelegate</span><span class="p">?</span><span class="o">.</span><span class="nf">didSetViewControllers</span><span class="p">?(</span><span class="n">viewControllers</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="n">animated</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note the <code class="language-plaintext highlighter-rouge">defer</code> in the functions that return a value. Swift makes it so easy to ensure an action is taken even after returning from a function. Or rather, after executing the call in the return statement, but before returning control to the calling code, the deferred code will be executed, which is just what we need, and allows us to keep our code concise and to the point.</p>

<p>With this in place, you make your observers conform to the PleasantNavigationControllerDelegate protocol, assign them as the navigation controller delegate at the appropriate time, and become master of navigation in your app.</p>

<p>You can find the full source code in <a href="https://gist.github.com/SintraWorks/8926164c71fa7ac2d8e54f8a78297d0f" title="PleasantNavigationController gist">this gist</a>, and the full repository, including a sample app <a href="https://github.com/SintraWorks/PleasantNavigationController">on GitHub</a></p>

<p>[Added link to gist on 12-01-2019; updated and added link to full repo on 14-09-2022]</p>]]></content><author><name></name></author><category term="swift" /><category term="uikit" /><category term="swift" /><category term="uikit" /><category term="iOS" /><summary type="html"><![CDATA[A navigation controller that does a better job at communicating navigation events.]]></summary></entry><entry><title type="html">Mini Auto Layout DSL Revisited</title><link href="https://sintraworks.github.io/swift/autolayout/2018/01/06/autolayout-dsl-v2.html" rel="alternate" type="text/html" title="Mini Auto Layout DSL Revisited" /><published>2018-01-06T00:00:00+00:00</published><updated>2018-01-06T00:00:00+00:00</updated><id>https://sintraworks.github.io/swift/autolayout/2018/01/06/autolayout-dsl-v2</id><content type="html" xml:base="https://sintraworks.github.io/swift/autolayout/2018/01/06/autolayout-dsl-v2.html"><![CDATA[<p>After the previous post, and despite the positive feedback, I wasn’t quite satisfied yet with the result and felt the code could do with some improvement. As I looked again at the <code class="language-plaintext highlighter-rouge">NSLayoutAnchor</code> header, I realised I had left out a bit of functionality. I also wanted to see if I could manage one more simplification. The end result is that, after some refactoring, the API surface has been slightly reduced, and, while the code has gained a few lines, it is more correct and more flexible.</p>

<p>I wanted to post about this refactoring sooner, but having sustained two broken ribs about a week ago slowed me down a bit… But anyway, here it is.</p>

<p>If you haven’t read <a href="/swift/autolayout/2017/12/27/autolayout-dsl.html">the original post</a> be sure to do so. It will make it easier to follow along here.</p>

<p>The first thing I wanted to do, which is something <a href="http://chris.eidhof.nl">Chris</a> also suggested, was to remove the distinction between <code class="language-plaintext highlighter-rouge">PairedConstraint</code> and <code class="language-plaintext highlighter-rouge">UnpairedConstraint</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">typealias</span> <span class="kt">Constraint</span> <span class="o">=</span> <span class="p">(</span><span class="n">_</span> <span class="nv">view</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">,</span> <span class="n">_</span> <span class="nv">otherView</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">NSLayoutConstraint</span>
</code></pre></div></div>

<p>This leaves us with a single type: <code class="language-plaintext highlighter-rouge">Constraint</code>, which takes two <code class="language-plaintext highlighter-rouge">UIView</code>s and returns a layout constraint. The second view (<code class="language-plaintext highlighter-rouge">otherView</code>) was made optional, because some constraints are not relative to other views. This compensates for the removal of the <code class="language-plaintext highlighter-rouge">UnpairedConstraint</code> type.</p>

<p>The definition of the <code class="language-plaintext highlighter-rouge">constraint</code> function for axis-relative constraints remains largely the same:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">constraint</span><span class="o">&lt;</span><span class="kt">Anchor</span><span class="p">,</span> <span class="kt">AnchorType</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">keyPath</span><span class="p">:</span> <span class="kt">KeyPath</span><span class="o">&lt;</span><span class="kt">UIView</span><span class="p">,</span> <span class="kt">Anchor</span><span class="o">&gt;</span><span class="p">,</span>
                                    <span class="n">_</span> <span class="nv">otherKeyPath</span><span class="p">:</span> <span class="kt">KeyPath</span><span class="o">&lt;</span><span class="kt">UIView</span><span class="p">,</span> <span class="kt">Anchor</span><span class="o">&gt;</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
                                    <span class="nv">constraintRelation</span><span class="p">:</span> <span class="kt">ConstraintRelation</span> <span class="o">=</span> <span class="o">.</span><span class="n">equal</span><span class="p">,</span>
                                    <span class="nv">multiplier</span><span class="p">:</span> <span class="kt">CGFloat</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
                                    <span class="nv">constant</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
                                    <span class="nv">priority</span><span class="p">:</span> <span class="kt">UILayoutPriority</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Constraint</span> <span class="k">where</span> <span class="kt">Anchor</span><span class="p">:</span> <span class="kt">NSLayoutAnchor</span><span class="o">&lt;</span><span class="kt">AnchorType</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span> <span class="n">view</span><span class="p">,</span> <span class="n">otherView</span> <span class="k">in</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">otherView</span> <span class="o">=</span> <span class="n">otherView</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Pairing view missing"</span><span class="p">)}</span>
        <span class="k">var</span> <span class="nv">partialConstraint</span><span class="p">:</span> <span class="kt">NSLayoutConstraint</span>
        <span class="k">let</span> <span class="nv">otherKeyPath</span> <span class="o">=</span> <span class="n">otherKeyPath</span> <span class="p">??</span> <span class="n">keyPath</span>
        
        <span class="k">switch</span> <span class="n">constraintRelation</span> <span class="p">{</span>
        <span class="k">case</span> <span class="o">.</span><span class="nv">equal</span><span class="p">:</span>
            <span class="n">partialConstraint</span> <span class="o">=</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span> <span class="n">otherView</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">otherKeyPath</span><span class="p">],</span> <span class="nv">constant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
        <span class="k">case</span> <span class="o">.</span><span class="nv">greaterThanOrEqual</span><span class="p">:</span>
            <span class="n">partialConstraint</span> <span class="o">=</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">greaterThanOrEqualTo</span><span class="p">:</span> <span class="n">otherView</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">otherKeyPath</span><span class="p">],</span> <span class="nv">constant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
        <span class="k">case</span> <span class="o">.</span><span class="nv">lessThanOrEqual</span><span class="p">:</span>
            <span class="n">partialConstraint</span> <span class="o">=</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">lessThanOrEqualTo</span><span class="p">:</span> <span class="n">otherView</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">otherKeyPath</span><span class="p">],</span> <span class="nv">constant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
        <span class="p">}</span>
        
        <span class="k">return</span> <span class="nf">fullConstraint</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">partialConstraint</span><span class="p">,</span> <span class="nv">withMultiplier</span><span class="p">:</span><span class="n">multiplier</span><span class="p">,</span> <span class="nv">priority</span><span class="p">:</span> <span class="n">priority</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It gained only a single line: since the closure’s second view parameter is now optional, we need to unwrap it. If unwrapping fails, we cop out, because not supplying a second view for this type of constraint is a programmer error.</p>

<p>The second <code class="language-plaintext highlighter-rouge">constraint</code> function, which creates constraints for <code class="language-plaintext highlighter-rouge">NSLayoutDimension</code> based anchors gets more heavily refactored, since this is where we add the missing functionality. In its previous incarnation the function could create constraints directly on constants, but I had left out the possibility of creating constraints based on another anchor and a constant. So lets fix that:</p>

<p>The function will need to either create a constraint of the <code class="language-plaintext highlighter-rouge">equalToConstant:</code> type (as it already did), or of the <code class="language-plaintext highlighter-rouge">equalTo:otherAnchor, constant:</code> type. To avoid a fair amount of duplication and typing, we define a nested function that will do the work of actually creating the constraint:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="kd">func</span> <span class="nf">constraint</span><span class="p">(</span><span class="nv">otherView</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">,</span>
                        <span class="nv">otherKeyPath</span><span class="p">:</span> <span class="kt">KeyPath</span><span class="o">&lt;</span><span class="kt">UIView</span><span class="p">,</span> <span class="kt">Anchor</span><span class="o">&gt;</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">NSLayoutConstraint</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nv">otherKeyPath</span> <span class="o">=</span> <span class="n">otherKeyPath</span> <span class="p">{</span>
                <span class="k">switch</span> <span class="n">constraintRelation</span> <span class="p">{</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">equal</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span><span class="n">otherView</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">otherKeyPath</span><span class="p">],</span> <span class="nv">constant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">greaterThanOrEqual</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">greaterThanOrEqualTo</span><span class="p">:</span><span class="n">otherView</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">otherKeyPath</span><span class="p">],</span> <span class="nv">constant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">lessThanOrEqual</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">lessThanOrEqualTo</span><span class="p">:</span><span class="n">otherView</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">otherKeyPath</span><span class="p">],</span> <span class="nv">constant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">switch</span> <span class="n">constraintRelation</span> <span class="p">{</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">equal</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalToConstant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">greaterThanOrEqual</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">greaterThanOrEqualToConstant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">lessThanOrEqual</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">lessThanOrEqualToConstant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
</code></pre></div></div>

<p>It takes a view, which is the related view, and an optional keyPath to the related anchor. The other parameters needed within the function body are inherited from the surrounding context. This saves quite a bit of hassle. The function first checks for the existence of a related key path. If there is none, we create the constant based constraint, as in the previous version of the code, ignoring the <code class="language-plaintext highlighter-rouge">otherView</code>, which is not needed here. If we did receive an <code class="language-plaintext highlighter-rouge">otherKeyPath</code>, we unwrap it and use it to create a constraint related to that key path and a constant, relative to the view we received in <code class="language-plaintext highlighter-rouge">otherView</code>.</p>

<p>What is left, is to call the internal function, which can be done quite succinctly, passing in correct values for <code class="language-plaintext highlighter-rouge">otherView</code> and <code class="language-plaintext highlighter-rouge">otherKeyPath</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="nf">constraint</span><span class="p">(</span><span class="nv">otherView</span><span class="p">:</span> <span class="n">otherView</span> <span class="p">??</span> <span class="n">view</span><span class="p">,</span>
                   <span class="nv">otherKeyPath</span><span class="p">:</span> <span class="n">otherView</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">?</span> <span class="nv">otherKeyPath</span> <span class="p">:</span> <span class="n">otherKeyPath</span> <span class="p">??</span> <span class="n">keyPath</span><span class="p">)</span>
</code></pre></div></div>

<p>If the outer function was called with an <code class="language-plaintext highlighter-rouge">otherView</code> we pass it along to the inner function. If the <code class="language-plaintext highlighter-rouge">otherView</code> is nil, we pass along the base view.</p>

<p>For <code class="language-plaintext highlighter-rouge">otherKeyPath</code>, if the outer function did not receive an <code class="language-plaintext highlighter-rouge">otherView</code> we pass in the <code class="language-plaintext highlighter-rouge">otherKeyPath</code>, which may or may not be <code class="language-plaintext highlighter-rouge">nil</code>. If we did receive another view, we are definitely creating a constraint based on a related anchor. Therefore we must pass in another key path. If one is upplied, we pass it along, if none is supplied, we use the same key path as that for the base view. This last trick allows us to create constraints without having to retype the anchor key path, thus simplifying the call site. I wrote it using a combination of the ternary and the nil coalescing operators. While I’m usually wary of doing that, I felt that, in this instance, the result is compact and quite readable.</p>

<p>Putting the pieces together, the whole function becomes:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">constraint</span><span class="o">&lt;</span><span class="kt">Anchor</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">keyPath</span><span class="p">:</span> <span class="kt">KeyPath</span><span class="o">&lt;</span><span class="kt">UIView</span><span class="p">,</span> <span class="kt">Anchor</span><span class="o">&gt;</span><span class="p">,</span>
                        <span class="n">_</span> <span class="nv">otherKeyPath</span><span class="p">:</span> <span class="kt">KeyPath</span><span class="o">&lt;</span><span class="kt">UIView</span><span class="p">,</span> <span class="kt">Anchor</span><span class="o">&gt;</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
                        <span class="nv">constraintRelation</span><span class="p">:</span> <span class="kt">ConstraintRelation</span> <span class="o">=</span> <span class="o">.</span><span class="n">equal</span><span class="p">,</span>
                        <span class="nv">multiplier</span><span class="p">:</span> <span class="kt">CGFloat</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
                        <span class="nv">constant</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
                        <span class="nv">priority</span><span class="p">:</span> <span class="kt">UILayoutPriority</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Constraint</span> <span class="k">where</span> <span class="kt">Anchor</span><span class="p">:</span> <span class="kt">NSLayoutDimension</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span> <span class="n">view</span><span class="p">,</span> <span class="n">otherView</span> <span class="k">in</span>
        <span class="kd">func</span> <span class="nf">constraint</span><span class="p">(</span><span class="nv">otherView</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">,</span>
                        <span class="nv">otherKeyPath</span><span class="p">:</span> <span class="kt">KeyPath</span><span class="o">&lt;</span><span class="kt">UIView</span><span class="p">,</span> <span class="kt">Anchor</span><span class="o">&gt;</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">NSLayoutConstraint</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nv">otherKeyPath</span> <span class="o">=</span> <span class="n">otherKeyPath</span> <span class="p">{</span>
                <span class="k">switch</span> <span class="n">constraintRelation</span> <span class="p">{</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">equal</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalTo</span><span class="p">:</span><span class="n">otherView</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">otherKeyPath</span><span class="p">],</span> <span class="nv">constant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">greaterThanOrEqual</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">greaterThanOrEqualTo</span><span class="p">:</span><span class="n">otherView</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">otherKeyPath</span><span class="p">],</span> <span class="nv">constant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">lessThanOrEqual</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">lessThanOrEqualTo</span><span class="p">:</span><span class="n">otherView</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">otherKeyPath</span><span class="p">],</span> <span class="nv">constant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">switch</span> <span class="n">constraintRelation</span> <span class="p">{</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">equal</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">equalToConstant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">greaterThanOrEqual</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">greaterThanOrEqualToConstant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="k">case</span> <span class="o">.</span><span class="nv">lessThanOrEqual</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">view</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span><span class="o">.</span><span class="nf">constraint</span><span class="p">(</span><span class="nv">lessThanOrEqualToConstant</span><span class="p">:</span> <span class="n">constant</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="nf">fullConstraint</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="nf">constraint</span><span class="p">(</span><span class="nv">otherView</span><span class="p">:</span> <span class="n">otherView</span> <span class="p">??</span> <span class="n">view</span><span class="p">,</span>
                                               <span class="nv">otherKeyPath</span><span class="p">:</span> <span class="n">otherView</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">?</span> <span class="nv">otherKeyPath</span> <span class="p">:</span> <span class="n">otherKeyPath</span> <span class="p">??</span> <span class="n">keyPath</span><span class="p">),</span>
                              <span class="nv">withMultiplier</span><span class="p">:</span><span class="n">multiplier</span><span class="p">,</span>
                              <span class="nv">priority</span><span class="p">:</span> <span class="n">priority</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The constraint creation helper function, which allows us to apply a multiplier and a priority, remains the same but was renamed to <code class="language-plaintext highlighter-rouge">fullConstraint</code> for clarity.</p>

<p>The extension on <code class="language-plaintext highlighter-rouge">UIView</code> remains largely the same. Only the body of the last function was refactored, to account for the removal of <code class="language-plaintext highlighter-rouge">UnpairedConstraint</code>. We now must pass in a <code class="language-plaintext highlighter-rouge">nil</code> second parameter:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIView</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">addSubview</span><span class="p">(</span><span class="n">_</span> <span class="nv">child</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">,</span> <span class="n">pairingTo</span> <span class="nv">pairingView</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">constraints</span><span class="p">:</span> <span class="p">[</span><span class="kt">Constraint</span><span class="p">])</span> <span class="p">{</span><span class="err">…</span><span class="p">}</span>
    <span class="kd">func</span> <span class="nf">constrainToView</span><span class="p">(</span><span class="n">_</span> <span class="nv">pairingView</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">,</span> <span class="nv">constraints</span><span class="p">:</span> <span class="p">[</span><span class="kt">Constraint</span><span class="p">])</span> <span class="p">{</span><span class="err">…</span><span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">constrain</span><span class="p">(</span><span class="n">to</span> <span class="nv">constraints</span><span class="p">:</span> <span class="p">[</span><span class="kt">Constraint</span><span class="p">])</span> <span class="p">{</span>
        <span class="kt">NSLayoutConstraint</span><span class="o">.</span><span class="nf">activate</span><span class="p">(</span><span class="n">constraints</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="nv">$0</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">})</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can now create additional constraint types, in a way that we couldn’t in the previous version of the code. For instance:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">purpleView</span><span class="o">.</span><span class="nf">constrain</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="p">[</span>
    <span class="nf">constraint</span><span class="p">(\</span><span class="o">.</span><span class="n">widthAnchor</span><span class="p">,</span> <span class="p">\</span><span class="o">.</span><span class="n">heightAnchor</span><span class="p">)</span>
    <span class="p">])</span>
	
</code></pre></div></div>

<p>You can find the full source code for this refactoring, with additional constraint examples, in a gist <a href="https://gist.github.com/SintraWorks/8955db5d0e687fd7450b90cf936d44a1">here</a>.</p>]]></content><author><name></name></author><category term="swift" /><category term="autolayout" /><category term="swift" /><category term="autolayout" /><category term="macOS" /><category term="iOS" /><summary type="html"><![CDATA[Running further with Chris Eidhof's Micro Auto Layout DSL.]]></summary></entry></feed>