<?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://fabernovel.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://fabernovel.github.io/" rel="alternate" type="text/html" /><updated>2026-07-07T15:49:21+00:00</updated><id>https://fabernovel.github.io/feed.xml</id><title type="html">EY Fabernovel</title><subtitle>Explore the EY Fabernovel tech team’s latest musings on mobile development, front end, back end, modern architecture, Cloud, APIs and so much more.</subtitle><author><name>EY Fabernovel</name><email>adv@fabernovel.ey.com</email></author><entry><title type="html">Let’s try to use Swift Concurrency to handle navigation in UIKit</title><link href="https://fabernovel.github.io/2026-07-07/let-s-try-to-use-swift-concurrency-to-handle-navigation-in-uikit" rel="alternate" type="text/html" title="Let’s try to use Swift Concurrency to handle navigation in UIKit" /><published>2026-07-07T00:00:00+00:00</published><updated>2026-07-07T00:00:00+00:00</updated><id>https://fabernovel.github.io/2026-07-07/let-s-try-to-use-swift-concurrency-to-handle-navigation-in-uikit</id><content type="html" xml:base="https://fabernovel.github.io/2026-07-07/let-s-try-to-use-swift-concurrency-to-handle-navigation-in-uikit"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>Over the last few years we have seen great changes in the way we handle asynchronous code with Swift Concurrency.
In 2021 it was introduced in Swift 5.5. In 2024 Swift 6.0 improved and enforced compilation checks. More recently in 2025, a new approachable mode was introduced to simplify its use in existing projects.
The fact that it was back-ported up to iOS 13.0 was a huge deal, and allowed us to start using this new
paradigm faster than usual for new features like that.</p>

<p>The network stack was a perfect candidate to start introducing async / await in our code base as it used
completion based APIs. But as we increase our usage of Swift Concurrency in our app (from Network to Business logic),
we usually stop right before the UI, where the UI is creating Tasks to run our asynchronous code.</p>

<p>In this article we will see how we can use Swift Concurrency to handle UIKit navigation logic, how it can improve
the readability, but we will also explore the complexity and challenges it brings.</p>

<h2 id="concept">Concept</h2>

<p>Let’s start simple with an alert, in UIKit we present a <code class="language-plaintext highlighter-rouge">UIAlertController</code> to show an alert, and we
are informed of the selected action with the <code class="language-plaintext highlighter-rouge">UIAlertAction</code>’s closure handler.
Here is what it looks like:</p>

<!-- (-> We could handle a user dismissal as a thrown exception, but too complex for now) -->

<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="c1">// ...</span>
    <span class="kd">func</span> <span class="nf">askUserForValue</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">alertController</span> <span class="o">=</span> <span class="kt">UIAlertController</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"Choose a value"</span><span class="p">,</span> <span class="nv">message</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">preferredStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">alert</span><span class="p">)</span>
        <span class="n">alertController</span><span class="o">.</span><span class="nf">addAction</span><span class="p">(</span><span class="kt">UIAlertAction</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"1"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">)</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">handleAlertSelection</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="s">"1"</span><span class="p">)</span>
        <span class="p">})</span>
        <span class="n">alertController</span><span class="o">.</span><span class="nf">addAction</span><span class="p">(</span><span class="kt">UIAlertAction</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"2"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">)</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">handleAlertSelection</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="s">"2"</span><span class="p">)</span>
        <span class="p">})</span>
        <span class="n">alertController</span><span class="o">.</span><span class="nf">addAction</span><span class="p">(</span><span class="kt">UIAlertAction</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"Cancel"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">cancel</span><span class="p">)</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">handleAlertSelection</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
        <span class="p">})</span>
        <span class="nf">present</span><span class="p">(</span><span class="n">alertController</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">handleAlertSelection</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span> <span class="p">{</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"Selection: </span><span class="se">\(</span><span class="n">value</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
        <span class="c1">// Do something with user selection</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This works, but we can see that the handling of the selection is outside the initial function.</p>

<p>Let’s look at how we can transform this completion based alert to use Swift Concurrency:</p>
<ul>
  <li>First we use <a href="https://developer.apple.com/documentation/swift/withcheckedcontinuation(isolation:function:_:)"><code class="language-plaintext highlighter-rouge">withCheckedContinuation(isolation:function:_:)</code></a> to bridge the completion to an async code:
    <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">UIViewController</span> <span class="p">{</span>
  <span class="c1">// ...</span>
  <span class="kd">func</span> <span class="nf">buttonAction</span><span class="p">()</span> <span class="k">async</span> <span class="p">{</span>
      <span class="k">let</span> <span class="nv">alertController</span> <span class="o">=</span> <span class="kt">UIAlertController</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"Choose a value"</span><span class="p">,</span> <span class="nv">message</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">preferredStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">alert</span><span class="p">)</span>
      <span class="k">let</span> <span class="nv">selection</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="k">await</span> <span class="n">withCheckedContinuation</span> <span class="p">{</span> <span class="n">continuation</span> <span class="k">in</span>
          <span class="n">alertController</span><span class="o">.</span><span class="nf">addAction</span><span class="p">(</span><span class="kt">UIAlertAction</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"1"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">)</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
              <span class="n">continuation</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">returning</span><span class="p">:</span> <span class="s">"1"</span><span class="p">)</span>
          <span class="p">})</span>
          <span class="n">alertController</span><span class="o">.</span><span class="nf">addAction</span><span class="p">(</span><span class="kt">UIAlertAction</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"2"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">)</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
              <span class="n">continuation</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">returning</span><span class="p">:</span> <span class="s">"2"</span><span class="p">)</span>
          <span class="p">})</span>
          <span class="n">alertController</span><span class="o">.</span><span class="nf">addAction</span><span class="p">(</span><span class="kt">UIAlertAction</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"Cancel"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">cancel</span><span class="p">)</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
              <span class="n">continuation</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">returning</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
          <span class="p">})</span>
          <span class="nf">present</span><span class="p">(</span><span class="n">alertController</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="nf">print</span><span class="p">(</span><span class="s">"Selection: </span><span class="se">\(</span><span class="n">selection</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
      <span class="c1">// Do something with user selection</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>Then we can create a utility method to simplify the creation of the alert:
    <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIViewController</span> <span class="p">{</span>
  <span class="kd">func</span> <span class="n">presentAsyncAlert</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Sendable</span><span class="o">&gt;</span><span class="p">(</span>
      <span class="nv">title</span><span class="p">:</span> <span class="kt">String</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="nv">preferredStyle</span><span class="p">:</span> <span class="kt">UIAlertController</span><span class="o">.</span><span class="kt">Style</span><span class="p">,</span>
      <span class="nv">actions</span><span class="p">:</span> <span class="p">[(</span><span class="nv">title</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="kt">UIAlertAction</span><span class="o">.</span><span class="kt">Style</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">T</span><span class="p">?)]</span>
  <span class="p">)</span> <span class="k">async</span> <span class="o">-&gt;</span> <span class="kt">T</span><span class="p">?</span> <span class="p">{</span>
      <span class="k">let</span> <span class="nv">alertController</span> <span class="o">=</span> <span class="kt">UIAlertController</span><span class="p">(</span>
          <span class="nv">title</span><span class="p">:</span> <span class="n">title</span><span class="p">,</span>
          <span class="nv">message</span><span class="p">:</span> <span class="n">message</span><span class="p">,</span>
          <span class="nv">preferredStyle</span><span class="p">:</span> <span class="n">preferredStyle</span>
      <span class="p">)</span>
      <span class="k">return</span> <span class="k">await</span> <span class="n">withCheckedContinuation</span> <span class="p">{</span> <span class="n">continuation</span> <span class="k">in</span>
          <span class="k">for</span> <span class="n">action</span> <span class="k">in</span> <span class="n">actions</span> <span class="p">{</span>
              <span class="k">let</span> <span class="nv">action</span> <span class="o">=</span> <span class="kt">UIAlertAction</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="n">action</span><span class="o">.</span><span class="n">title</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="n">action</span><span class="o">.</span><span class="n">style</span><span class="p">)</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
                  <span class="n">continuation</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">returning</span><span class="p">:</span> <span class="n">action</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
              <span class="p">}</span>
              <span class="n">alertController</span><span class="o">.</span><span class="nf">addAction</span><span class="p">(</span><span class="n">action</span><span class="p">)</span>
          <span class="p">}</span>
          <span class="nf">present</span><span class="p">(</span><span class="n">alertController</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="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>And finally we can simplify our call site:
    <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="c1">// ...</span>
  <span class="kd">func</span> <span class="nf">buttonAction</span><span class="p">()</span> <span class="k">async</span> <span class="p">{</span>
      <span class="k">let</span> <span class="nv">selection</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">presentAsyncAlert</span><span class="p">(</span>
          <span class="nv">title</span><span class="p">:</span> <span class="s">"Choose a value"</span><span class="p">,</span>
          <span class="nv">message</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span>
          <span class="nv">preferredStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">alert</span><span class="p">,</span>
          <span class="nv">actions</span><span class="p">:</span> <span class="p">[</span>
              <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"1"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="s">"1"</span><span class="p">),</span>
              <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"2"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="s">"2"</span><span class="p">),</span>
              <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"Cancel"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">cancel</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
          <span class="p">]</span>
      <span class="p">)</span>
      <span class="nf">print</span><span class="p">(</span><span class="s">"Selection: </span><span class="se">\(</span><span class="n">selection</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
      <span class="c1">// Do something with user selection</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ul>

<p>With this async version of the code we can already see the similarities with async network calls.
Instead of having to deal with callbacks, we can focus on the logic and reading of the function line by line
instead of jumping to multiple function.</p>

<p>Using the same methodology let’s see what we can obtain for a more complex (and realistic) situation, after that
we will see what we need to have in mind with this kind of bridging.</p>

<h2 id="using-the-concept-on-something-useful">Using the concept on something useful</h2>

<p>With this new async API for displaying alert, it is now easy to do something like a quiz.
Handling selected actions for each alert and chaining multiple alerts becomes trivial.
As we can control alerts from an asynchronous context, it is also really easy to perform
network calls, like used in this example to validate answers.</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">UIViewController</span> <span class="p">{</span>
    <span class="c1">// ...</span>
    <span class="kd">func</span> <span class="nf">startQuiz</span><span class="p">()</span> <span class="k">async</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">answer1</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">presentAsyncAlert</span><span class="p">(</span>
            <span class="nv">title</span><span class="p">:</span> <span class="s">"Question 1"</span><span class="p">,</span>
            <span class="nv">message</span><span class="p">:</span> <span class="s">"Which keyword do you use to declare constants in Swift ?"</span><span class="p">,</span>
            <span class="nv">preferredStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">alert</span><span class="p">,</span>
            <span class="nv">actions</span><span class="p">:</span> <span class="p">[</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"var"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">1</span><span class="p">),</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"val"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">2</span><span class="p">),</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"let"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">3</span><span class="p">),</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"const"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span>
            <span class="p">]</span>
        <span class="p">)</span>
        <span class="k">let</span> <span class="nv">answer2</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">presentAsyncAlert</span><span class="p">(</span>
            <span class="nv">title</span><span class="p">:</span> <span class="s">"Question 2"</span><span class="p">,</span>
            <span class="nv">message</span><span class="p">:</span> <span class="s">"What size in bits is using Int in Swift ?"</span><span class="p">,</span>
            <span class="nv">preferredStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">alert</span><span class="p">,</span>
            <span class="nv">actions</span><span class="p">:</span> <span class="p">[</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"16"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">1</span><span class="p">),</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"32"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">2</span><span class="p">),</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"64"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">3</span><span class="p">),</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"128"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span>
            <span class="p">]</span>
        <span class="p">)</span>
        <span class="k">let</span> <span class="nv">answer3</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">presentAsyncAlert</span><span class="p">(</span>
            <span class="nv">title</span><span class="p">:</span> <span class="s">"Question 3"</span><span class="p">,</span>
            <span class="nv">message</span><span class="p">:</span> <span class="s">"Which keyword does not exist in Swift ?"</span><span class="p">,</span>
            <span class="nv">preferredStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">alert</span><span class="p">,</span>
            <span class="nv">actions</span><span class="p">:</span> <span class="p">[</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"break"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">1</span><span class="p">),</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"continue"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">2</span><span class="p">),</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"goto"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">3</span><span class="p">),</span>
                <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"return"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span>
            <span class="p">]</span>
        <span class="p">)</span>
        <span class="k">var</span> <span class="nv">request</span> <span class="o">=</span> <span class="kt">URLRequest</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="s">"http://127.0.0.1/answer"</span><span class="p">)</span><span class="o">!</span><span class="p">)</span>
        <span class="n">request</span><span class="o">.</span><span class="n">httpMethod</span> <span class="o">=</span> <span class="s">"POST"</span>
        <span class="n">request</span><span class="o">.</span><span class="n">httpBody</span> <span class="o">=</span> <span class="s">"answers=</span><span class="se">\(</span><span class="n">answer1</span><span class="se">)</span><span class="s">,</span><span class="se">\(</span><span class="n">answer2</span><span class="se">)</span><span class="s">,</span><span class="se">\(</span><span class="n">answer3</span><span class="se">)</span><span class="s">"</span><span class="o">.</span><span class="nf">data</span><span class="p">(</span><span class="nv">using</span><span class="p">:</span> <span class="o">.</span><span class="n">utf8</span><span class="p">)</span>
        <span class="k">do</span> <span class="p">{</span>
            <span class="k">let</span> <span class="p">(</span><span class="nv">data</span><span class="p">,</span> <span class="nv">_</span><span class="p">)</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="kt">URLSession</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="nf">data</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">request</span><span class="p">)</span>
            <span class="n">_</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">presentAsyncAlert</span><span class="p">(</span>
                <span class="nv">title</span><span class="p">:</span> <span class="s">"Your score"</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="nv">data</span><span class="p">:</span> <span class="n">data</span><span class="p">,</span> <span class="nv">encoding</span><span class="p">:</span> <span class="o">.</span><span class="n">utf8</span><span class="p">),</span>
                <span class="nv">preferredStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">alert</span><span class="p">,</span>
                <span class="nv">actions</span><span class="p">:</span> <span class="p">[</span>
                    <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"ok"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
                <span class="p">]</span>
            <span class="p">)</span>
        <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
            <span class="n">_</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">presentAsyncAlert</span><span class="p">(</span>
                <span class="nv">title</span><span class="p">:</span> <span class="s">"Error"</span><span class="p">,</span>
                <span class="nv">message</span><span class="p">:</span> <span class="n">error</span><span class="o">.</span><span class="n">localizedDescription</span><span class="p">,</span>
                <span class="nv">preferredStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">alert</span><span class="p">,</span>
                <span class="nv">actions</span><span class="p">:</span> <span class="p">[</span>
                    <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"ok"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="mi">1</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>Playground with this example available <a href="/assets/let-s-try-to-use-swift-concurrency-to-handle-navigation-in-uikit/quiz_example.playground.zip">here</a>.</p>

<p>By using the same method we used to handle presentation of alerts in an async way: we can also handle the navigation to any kind of views.
We could imagine having this kind of order preparation code, that collects all required information in a concise and readable way:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">prepareOrder</span><span class="p">(</span><span class="k">for</span> <span class="nv">products</span><span class="p">:</span> <span class="p">[</span><span class="kt">Product</span><span class="p">])</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">PreparedOrder</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">userAccount</span><span class="p">:</span> <span class="kt">UserAccount</span>
    <span class="k">if</span> <span class="k">let</span> <span class="nv">account</span> <span class="o">=</span> <span class="nf">getLoggedInAccount</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">userAccount</span> <span class="o">=</span> <span class="n">account</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">userAccount</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="nf">presentLogInForm</span><span class="p">()</span>
    <span class="p">}</span>
    <span class="k">try</span> <span class="k">await</span> <span class="nf">validateProductStockNetworkCall</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">products</span><span class="p">)</span>
    <span class="k">let</span> <span class="nv">shippingAddress</span><span class="p">:</span> <span class="kt">Address</span>
    <span class="k">if</span> <span class="k">let</span> <span class="nv">address</span> <span class="o">=</span> <span class="n">userAccount</span><span class="o">.</span><span class="n">shippingAddress</span> <span class="p">{</span>
        <span class="n">shippingAddress</span> <span class="o">=</span> <span class="n">address</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">shippingAddress</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="nf">presentShippingAddressForm</span><span class="p">()</span>
    <span class="p">}</span>
    <span class="k">let</span> <span class="nv">paymentMethod</span><span class="p">:</span> <span class="kt">PaymentMethod</span>
    <span class="k">if</span> <span class="k">let</span> <span class="nv">payment</span> <span class="o">=</span> <span class="n">userAccount</span><span class="o">.</span><span class="n">paymentMethod</span> <span class="p">{</span>
        <span class="n">paymentMethod</span> <span class="o">=</span> <span class="n">payment</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">paymentMethod</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="nf">presentPaymentMethodForm</span><span class="p">()</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="kt">PreparedOrder</span><span class="p">(</span>
        <span class="nv">userAccount</span><span class="p">:</span> <span class="n">userAccount</span><span class="p">,</span>
        <span class="nv">products</span><span class="p">:</span> <span class="n">products</span><span class="p">,</span>
        <span class="nv">shippingAddress</span><span class="p">:</span> <span class="n">shippingAddress</span><span class="p">,</span>
        <span class="nv">paymentMethod</span><span class="p">:</span> <span class="n">paymentMethod</span>
    <span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="problems-and-issues-with-handling-navigation-this-way">Problems and issues with handling navigation this way</h2>

<p>In the previous examples we have seen that handling navigation with async methods can make the code
very easy to follow. But before going too far, let’s see what issues we have with the current simple approach.</p>

<h3 id="requirements-from-withcheckedcontinuation">Requirements from <code class="language-plaintext highlighter-rouge">withCheckedContinuation</code></h3>

<p><code class="language-plaintext highlighter-rouge">withCheckedContinuation</code> has a very important requirement in its <a href="https://developer.apple.com/documentation/swift/withcheckedcontinuation(isolation:function:_:)">documentation</a>:</p>
<blockquote>
  <p>You must invoke the continuation’s resume method exactly once.</p>
</blockquote>

<p>Failing to satisfy it has consequences also described in the documentation:</p>

<blockquote>
  <p>Failing to invoke it (eventually) will cause the calling task to remain suspended indefinitely which will result in the task “hanging” as well as being leaked with no possibility to destroy it.</p>
</blockquote>

<p>In the alert example, having a programmatic dismissal of the alert will fail this requirement, as the
continuation was only resumed after the user selected an action. Doing so will show this warning in the console:</p>
<blockquote>
  <p>SWIFT TASK CONTINUATION MISUSE: presentAsyncAlert() leaked its continuation without resuming it. This may cause tasks waiting on it to remain suspended forever.</p>
</blockquote>

<h3 id="using-deinit-to-force-the-continuation-to-resume">Using deinit to force the continuation to resume</h3>

<p>One idea to prevent this hanging continuation is to resume it when the presented view controller is deinit.
But this is more complex to do with Swift Concurrency because it is very easy to retain values in Task contexts.</p>

<p>Imagine we have a <code class="language-plaintext highlighter-rouge">SamplePickerViewController</code> we want to present over ContentViewController, here a simple implementation causing that creates a retain cycle.</p>

<p>Here we have a custom view controller that lets the user pick some value, like we could have done with an alert.
With the deinit resuming the continuation to prevent continuation misuse.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">SamplePickerViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">continuation</span><span class="p">:</span> <span class="kt">CheckedContinuation</span><span class="o">&lt;</span><span class="kt">Int</span><span class="p">?,</span> <span class="kt">Never</span><span class="o">&gt;</span><span class="p">?</span> <span class="c1">// 5</span>

    <span class="kd">deinit</span> <span class="p">{</span>
        <span class="c1">// This does not work due to a retain cycle</span>
        <span class="n">continuation</span><span class="p">?</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">returning</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">handleSelection</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?)</span> <span class="p">{</span>
        <span class="n">continuation</span><span class="p">?</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">returning</span><span class="p">:</span> <span class="n">value</span><span class="p">)</span>
        <span class="n">continuation</span> <span class="o">=</span> <span class="kc">nil</span>
    <span class="p">}</span>

    <span class="c1">// Some more code to handle UI and buttons that calls `handleSelection(_:)`</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="kt">ContentViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
    <span class="c1">// This implementation cause a retain cycle between the SamplePickerViewController and the Swift continuation</span>
    <span class="kd">func</span> <span class="nf">chooseValueFromSimplePicker</span><span class="p">()</span> <span class="p">{</span>
        <span class="kt">Task</span> <span class="p">{</span> <span class="c1">// 1</span>
            <span class="k">let</span> <span class="nv">viewController</span> <span class="o">=</span> <span class="kt">SamplePickerViewController</span><span class="p">()</span> <span class="c1">// 2</span>
            <span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="k">await</span> <span class="n">withCheckedContinuation</span> <span class="p">{</span> <span class="n">continuation</span> <span class="k">in</span> <span class="c1">// 3</span>
                <span class="n">viewController</span><span class="o">.</span><span class="n">continuation</span> <span class="o">=</span> <span class="n">continuation</span> <span class="c1">// 4</span>
                <span class="nf">present</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="kc">true</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="nf">dismiss</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="nf">print</span><span class="p">(</span><span class="s">"Do something with value:"</span><span class="p">,</span> <span class="n">value</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>With this simple implementation, it is as simple as dismissing the sheet modal from a gesture to reveal a retain cycle.
We can see it in the memory graph:</p>

<figure>
    <img src="/assets/let-s-try-to-use-swift-concurrency-to-handle-navigation-in-uikit/SamplePickerViewController-retain-cycle.png" alt="Retain cycle between the SamplePickerViewController and the Task context of the continuation" />
    <figcaption>Retain cycle between the SamplePickerViewController and the Task context of the continuation</figcaption>
</figure>

<p>Here what is happening:</p>
<ul>
  <li>A Task context is created (1)</li>
  <li>An instance of SamplePickerViewController is created inside the task context (2), the task context retains it in memory</li>
  <li>A continuation is created to pause the current Task execution (3)</li>
  <li>The continuation is stored in the view controller (4)</li>
</ul>

<p>This created this retain cycle in memory:</p>
<ul>
  <li>SamplePickerViewController.continuation retains the continuation (5)</li>
  <li>The continuation retains the task context (3)</li>
  <li>The task context retains the view controller (2), which complete the cycle</li>
</ul>

<p>Here a solution using a forced unwrap to remove the SamplePickerViewController reference from the Task context:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ContentViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">improvedImplementationNotCausingRetainCycle</span><span class="p">()</span> <span class="p">{</span>
        <span class="kt">Task</span> <span class="p">{</span>
            <span class="k">var</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">SamplePickerViewController</span><span class="o">!</span> <span class="o">=</span> <span class="kt">SamplePickerViewController</span><span class="p">()</span> <span class="c1">// this let is now a var</span>
            <span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="k">await</span> <span class="n">withCheckedContinuation</span> <span class="p">{</span> <span class="n">continuation</span> <span class="k">in</span>
                <span class="n">viewController</span><span class="o">.</span><span class="n">continuation</span> <span class="o">=</span> <span class="n">continuation</span>
                <span class="nf">present</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="kc">true</span><span class="p">)</span>
                <span class="n">viewController</span> <span class="o">=</span> <span class="kc">nil</span> <span class="c1">// We release the value to prevent a retain cycle</span>
            <span class="p">}</span>
            <span class="c1">// We only want to dismiss if we are still presenting the view controller</span>
            <span class="k">if</span> <span class="n">presentedViewController</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
                <span class="nf">dismiss</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="nf">print</span><span class="p">(</span><span class="s">"Do something with value:"</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="nf">print</span><span class="p">(</span><span class="s">"Do something with value:"</span><span class="p">,</span> <span class="n">value</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>Preventing retain cycle with use of Task and withCheckedContinuation is possible, by manually handling
reference retains in the asynchronous context. However, this approach is complex and error-prone, let’s see
another simpler way to ensure proper usage of <code class="language-plaintext highlighter-rouge">withCheckedContinuation</code>.</p>

<h3 id="swift-concurrency-cancellation-model-to-the-rescue">Swift Concurrency cancellation model to the rescue</h3>

<p>We have seen that enforcing that continuation is always resumed is hard. One way to prevent those problems
is to use the Task Cancellation mechanism and force the asynchronous operation to throw with a <code class="language-plaintext highlighter-rouge">CancellationError</code>.</p>

<p>Handling the cancellation ensures that when a view disappears from the screen we cancel all pending
operation, and prevent any Task from being stuck permanently.</p>

<p>To participate in the cancellation we need to use the method <a href="https://developer.apple.com/documentation/swift/withtaskcancellationhandler(operation:oncancel:isolation:)"><code class="language-plaintext highlighter-rouge">withTaskCancellationHandler(operation:onCancel:)</code></a>.
The <code class="language-plaintext highlighter-rouge">onCancel</code> closure parameter is called when the task is cancelled, allowing us to resume the
continuation in a cancelled state.</p>

<p>Handling the cancellation with the alert presentation can be implemented with:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIViewController</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="n">presentAsyncAlertWithCancellationHandling</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span><span class="p">(</span>
        <span class="nv">title</span><span class="p">:</span> <span class="kt">String</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="nv">preferredStyle</span><span class="p">:</span> <span class="kt">UIAlertController</span><span class="o">.</span><span class="kt">Style</span><span class="p">,</span>
        <span class="nv">actions</span><span class="p">:</span> <span class="p">[(</span><span class="nv">title</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="kt">UIAlertAction</span><span class="o">.</span><span class="kt">Style</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">T</span><span class="p">)]</span>
    <span class="p">)</span> <span class="k">async</span> <span class="nf">throws</span><span class="p">(</span><span class="kt">CancellationError</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">T</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">cancellationHandler</span><span class="p">:</span> <span class="p">(</span><span class="kd">@MainActor</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)?</span>
        <span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">withTaskCancellationHandler</span> <span class="p">{</span>
            <span class="k">await</span> <span class="n">withCheckedContinuation</span> <span class="p">{</span> <span class="p">(</span><span class="nv">continuation</span><span class="p">:</span> <span class="kt">CheckedContinuation</span><span class="o">&lt;</span><span class="kt">Result</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">,</span> <span class="kt">CancellationError</span><span class="o">&gt;</span><span class="p">,</span> <span class="kt">Never</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">in</span>
                <span class="k">guard</span> <span class="o">!</span><span class="kt">Task</span><span class="o">.</span><span class="n">isCancelled</span><span class="p">,</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="n">continuation</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">returning</span><span class="p">:</span> <span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="kt">CancellationError</span><span class="p">()))</span>
                    <span class="k">return</span>
                <span class="p">}</span>
                <span class="k">let</span> <span class="nv">alertController</span> <span class="o">=</span> <span class="kt">UIAlertController</span><span class="p">(</span>
                    <span class="nv">title</span><span class="p">:</span> <span class="n">title</span><span class="p">,</span>
                    <span class="nv">message</span><span class="p">:</span> <span class="n">message</span><span class="p">,</span>
                    <span class="nv">preferredStyle</span><span class="p">:</span> <span class="n">preferredStyle</span>
                <span class="p">)</span>
                <span class="k">for</span> <span class="n">action</span> <span class="k">in</span> <span class="n">actions</span> <span class="p">{</span>
                    <span class="n">alertController</span><span class="o">.</span><span class="nf">addAction</span><span class="p">(</span><span class="kt">UIAlertAction</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="n">action</span><span class="o">.</span><span class="n">title</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="n">action</span><span class="o">.</span><span class="n">style</span><span class="p">)</span> <span class="p">{</span> <span class="kd">@MainActor</span><span class="p">()</span> <span class="n">_</span> <span class="k">in</span>
                        <span class="n">cancellationHandler</span> <span class="o">=</span> <span class="kc">nil</span>
                        <span class="n">continuation</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">returning</span><span class="p">:</span> <span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">action</span><span class="o">.</span><span class="n">value</span><span class="p">))</span>
                    <span class="p">})</span>
                <span class="p">}</span>
                <span class="nf">present</span><span class="p">(</span><span class="n">alertController</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="n">cancellationHandler</span> <span class="o">=</span> <span class="p">{</span>
                    <span class="n">alertController</span><span class="o">.</span><span class="n">presentingViewController</span><span class="p">?</span><span class="o">.</span><span class="nf">dismiss</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="n">continuation</span><span class="o">.</span><span class="nf">resume</span><span class="p">(</span><span class="nv">returning</span><span class="p">:</span> <span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="kt">CancellationError</span><span class="p">()))</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span> <span class="nv">onCancel</span><span class="p">:</span> <span class="p">{</span>
            <span class="kt">Task</span> <span class="p">{</span> <span class="kd">@MainActor</span> <span class="k">in</span>
                <span class="nf">cancellationHandler</span><span class="p">?()</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="k">try</span> <span class="n">result</span><span class="o">.</span><span class="nf">get</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And here how we could use it from a view controller, with automatic task cancellation when it disappears from the screen.</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">UIViewController</span> <span class="p">{</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">runningAsyncOperations</span><span class="p">:</span> <span class="kt">Set</span><span class="o">&lt;</span><span class="kt">Task</span><span class="o">&lt;</span><span class="kt">Void</span><span class="p">,</span> <span class="kt">Never</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidDisappear</span><span class="p">(</span><span class="n">_</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="k">super</span><span class="o">.</span><span class="nf">viewDidDisappear</span><span class="p">(</span><span class="n">animated</span><span class="p">)</span>
        <span class="n">runningAsyncOperations</span><span class="o">.</span><span class="n">forEach</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// MARK: - Private</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">addAsyncOperation</span><span class="p">(</span><span class="n">_</span> <span class="nv">operation</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="kd">@MainActor</span> <span class="p">()</span> <span class="k">async</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Task</span><span class="o">&lt;</span><span class="kt">Void</span><span class="p">,</span> <span class="kt">Never</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">task</span> <span class="o">=</span> <span class="kt">Task</span><span class="p">(</span><span class="nv">operation</span><span class="p">:</span> <span class="n">operation</span><span class="p">)</span>
        <span class="n">runningAsyncOperations</span><span class="o">.</span><span class="nf">insert</span><span class="p">(</span><span class="n">task</span><span class="p">)</span>
        <span class="kt">Task</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">await</span> <span class="n">task</span><span class="o">.</span><span class="n">value</span>
            <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">runningAsyncOperations</span><span class="o">.</span><span class="nf">remove</span><span class="p">(</span><span class="n">task</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">task</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">showSomeAlert</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">addAsyncOperation</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">selection</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="nf">presentAsyncAlertWithCancellationHandling</span><span class="p">(</span>
                <span class="nv">title</span><span class="p">:</span> <span class="s">"Choose a value"</span><span class="p">,</span>
                <span class="nv">message</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span>
                <span class="nv">preferredStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">alert</span><span class="p">,</span>
                <span class="nv">actions</span><span class="p">:</span> <span class="p">[</span>
                    <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"1"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="s">"1"</span><span class="p">),</span>
                    <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"2"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="k">default</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="s">"2"</span><span class="p">),</span>
                    <span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"Cancel"</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">cancel</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
                <span class="p">]</span>
            <span class="p">)</span>
            <span class="nf">print</span><span class="p">(</span><span class="s">"Selection: </span><span class="se">\(</span><span class="n">selection</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
            <span class="c1">// Do something with user selection</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>Trying to use the Swift Concurrency to handle the navigation in an iOS application was an interesting
journey, but with many unexpected traps.
From simple alert handling and the quiz example we have seen that it is really helpful to make understandable code.
But those examples did not handle many edge cases, some programmatic navigation could prevent our task from continuing,
the fact that it is very hard to not retain our view controllers while in Tasks. Those combined risking retain cycles.
And finally some relief with the cancellation mechanism, that could be used to prevent our issues by cancelling our
continuations in <code class="language-plaintext highlighter-rouge">viewDidDisappear()</code>.</p>

<p>With this article we have seen that Swift Concurrency can be used to make navigation code easy to
follow within a single method, even for multiple screens chained together. This ease of reading comes
at the price of the expectation of <code class="language-plaintext highlighter-rouge">CheckedContinuation</code> that require to always be resumed once or
some Task will be forever suspended.</p>

<p>Using async navigation for simple navigation, like alerts or custom modals, can simplify your code
while not causing too many issues when properly handling cancellation.
Trying to handle more complex navigation, like a multi screen form, seems inappropriate as it
increases the chances of misuse of the pattern and risk causing more harm than good.</p>

<!-- WIP: Working code, but probably too complex as an example

This method show an alert with supports for dismissal on task cancellation, and support for throwing a value when the presented alert is deinit.
```swift
@MainActor
func presentAsyncAlert_<T>(
    on viewController: Weak<UIViewController>,
    title: String,
    message: String?,
    preferredStyle: UIAlertController.Style,
    actions: [(title: String, style: UIAlertAction.Style, value: T)]
) async throws(CancellationError) -> T {
    var optionalAlertController: UIAlertController? = UIAlertController(
        title: title,
        message: message,
        preferredStyle: preferredStyle
    )
    weak let weakAlertController: UIAlertController? = optionalAlertController
    var currentContinuation: CheckedContinuation<Result<T, CancellationError>, Never>?
    let resumeContinuationWithValue: @MainActor (sending Result<T, CancellationError>) -> Void = { value in
        guard let continuation = currentContinuation else { return }
        currentContinuation = nil
        continuation.resume(returning: value)
    }
    let result = await withTaskCancellationHandler {
        await withCheckedContinuation { (continuation: CheckedContinuation<Result<T, CancellationError>, Never>) in
            currentContinuation = continuation
            guard let viewController = viewController.object, let alertController = optionalAlertController else {
                resumeContinuationWithValue(.failure(CancellationError()))
                return
            }
            for action in actions {
                alertController.addAction(UIAlertAction(title: action.title, style: action.style) { @MainActor() _ in
                    resumeContinuationWithValue(.success(action.value))
                })
            }
            viewController.present(alertController, animated: true)
            optionalAlertController = nil
            currentContinuation = continuation
            alertController.registerCallbackForDealloc {
                resumeContinuationWithValue(.failure(CancellationError()))
            }
        }
    } onCancel: {
        Task { @MainActor in
            weakAlertController?.presentingViewController?.dismiss(animated: true)
            resumeContinuationWithValue(.failure(CancellationError()))
        }
    }
    return try result.get()
}

extension UIViewController {
    private var deallocObserver: DeallocObserver? {
        get {
            return objc_getAssociatedObject(
                self,
                &(ContextAssociatedKeys.deallocObserver)
            ) as? DeallocObserver
        }
        set {
            guard deallocObserver == nil || newValue == nil else {
                preconditionFailure("Only one dealloc observer can be set per UIViewController")
            }
            objc_setAssociatedObject(
                self,
                &(ContextAssociatedKeys.deallocObserver),
                newValue,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC
            )
        }
    }

    /**
     * Associates an observer to the deallocation of self
     * - parameter callback: The callback triggered when self is deallocated
     */
    func registerCallbackForDealloc(_ callback: @escaping () -> Void) {
        deallocObserver = DeallocObserver(callback: callback)
    }

    /**
     * Removes the current deallocation observer
     */
    func removeCallbackForDealloc() {
        deallocObserver?.invalidate()
        deallocObserver = nil
    }
}

private enum ContextAssociatedKeys {
    @MainActor
    fileprivate static var deallocObserver: UInt8 = 0
}

final class DeallocObserver {

    private var callback: () -> Void

    init(callback: @escaping () -> Void) {
        self.callback = callback
    }

    func invalidate() {
        callback = {}
    }

    deinit {
        callback()
    }
}
``` -->

<!-- Way more generic, but way too complex

This core present an arbitrary view controller with cancellation and deinit support
```swift
class YourViewController: UIViewController {
    private var currentTasks: Set<Task<Void, Never>> = []

    private func autoCancelledTask(_ block: @MainActor @escaping () async -> Void) {
        let task = Task(operation: block)
        currentTasks.insert(task)
        Task { [weak self] in
            await task.value
            self?.currentTasks.remove(task)
        }
    }

    deinit {
        currentTasks.forEach { $0.cancel() }
    }

    private func showModal() {
        autoCancelledTask { [weak self] in
            if self == nil { return }
            let delegate = PushPickerDelegate()
            let value = try? await presentAsync(
                { UIKitValuePickerViewController() },
                on: Weak(self)
            ) { vc, resultProvider in
                vc.delegate = delegate
                delegate.continuation = resultProvider
            }
            self?.viewModel.modalSelection = value
        }
    }
}

@MainActor
func presentAsync<ViewController: UIViewController, T>(
    _ presentedViewController: () -> ViewController,
    on viewController: Weak<UIViewController>,
    returnBeforeDismissalCompletion: Bool = false,
    valueProvider: @escaping (ViewController, @MainActor @escaping (sending Result<T, CancellationError>) -> Void) -> Void
) async throws(CancellationError) -> T {
    var optionalPresentedController: ViewController? = presentedViewController()
    weak let weakPresentedController: ViewController? = optionalPresentedController
    var currentContinuation: CheckedContinuation<Result<T, CancellationError>, Never>?
    let resumeContinuationWithValue: @MainActor (sending Result<T, CancellationError>) -> Void = { value in
        guard let continuation = currentContinuation else { return }
        currentContinuation = nil
        continuation.resume(returning: value)
    }
    let result = await withTaskCancellationHandler {
        await withCheckedContinuation { (continuation: CheckedContinuation<Result<T, CancellationError>, Never>) in
            currentContinuation = continuation
            guard let viewController = viewController.object,
                  let presentedController = optionalPresentedController
            else {
                resumeContinuationWithValue(.failure(CancellationError()))
                return
            }
            valueProvider(presentedController) { result in
                if returnBeforeDismissalCompletion {
                    weakPresentedController?.presentingViewController?.dismiss(animated: true)
                    resumeContinuationWithValue(result)
                } else if let presented = weakPresentedController,
                          let presentingViewController = presented.presentingViewController {
                    Task {
                        await withCheckedContinuation { continuation in
                            presentingViewController.dismiss(animated: true) { continuation.resume() }
                        }
                        resumeContinuationWithValue(result)
                        // This ensures the controller was not deinit before we returned the result the line above
                        _ = presented
                    }
                } else {
                    resumeContinuationWithValue(result)
                }
            }
            viewController.present(presentedController, animated: true)
            optionalPresentedController = nil
            currentContinuation = continuation
            presentedController.registerCallbackForDealloc {
                resumeContinuationWithValue(.failure(CancellationError()))
            }
        }
    } onCancel: {
        Task { @MainActor in
            let result: Result<T, CancellationError> = .failure(CancellationError())
            if returnBeforeDismissalCompletion {
                weakPresentedController?.presentingViewController?.dismiss(animated: true)
                resumeContinuationWithValue(result)
            } else if let presented = weakPresentedController,
                      let presentingViewController = presented.presentingViewController {
                Task {
                    await withCheckedContinuation { continuation in
                        presentingViewController.dismiss(animated: true) { continuation.resume() }
                    }
                    resumeContinuationWithValue(result)
                    // This ensures the controller was not deinit before we returned the result the line above
                    _ = presented
                }
            } else {
                resumeContinuationWithValue(result)
            }
        }
    }
    return try result.get()
}
``` -->]]></content><author><name>Alexandre Podlewski</name></author><category term="Swift" /><category term="iOS" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">I tried the Julia Language during a month</title><link href="https://fabernovel.github.io/2022-01-30/i-tried-the-julia-language-during-a-month" rel="alternate" type="text/html" title="I tried the Julia Language during a month" /><published>2022-01-30T00:00:00+00:00</published><updated>2022-01-30T00:00:00+00:00</updated><id>https://fabernovel.github.io/2022-01-30/i-tried-the-julia-language-during-a-month</id><content type="html" xml:base="https://fabernovel.github.io/2022-01-30/i-tried-the-julia-language-during-a-month"><![CDATA[<p>Hi, I’m Roland, machine learning engineer at Fabernovel and this December I have decided to rediscover the <a href="https://adventofcode.com/">AdventOfCode</a> adventure. AdventOfCode has offered an advent calendar every year since 2015 but instead of receiving a piece of chocolate every day, we receive a riddle (separated into 2 parts) that must be solved by coding. At Fabernovel, several of us have participated and it’s nice to share with others the blockers and solutions day after day.</p>

<p>This year, I chose to increase the difficulty a bit by solving the challenges in Julia, a programming language that I did not know at all. This was the opportunity to discover this language that some perceive as the successor of Python.</p>

<p>Julia’s choice is not random: it is recent, attractive and close to the Python language that I’m familiar with, so I expect not to struggle too much with it. It is apparently mathematically oriented, which makes it an ideal companion for algorithmic challenges. I can’t wait to see it!</p>

<p>Come on, let’s install Julia on my computer.</p>

<h2 id="julia-installation">Julia installation</h2>

<p>I did my challenge last year with <a href="https://jupyter.org/">Jupyter notebooks</a> in Python and it went well. As a reminder, a notebook allows you to run separate pieces of code and have their results displayed on a single document. It is used a lot in data science: it is very convenient for testing things and having nice visualizations integrated into the code.
I really want to do the same with Julia knowing that Jupyter’s “ju” designates Julia so it shouldn’t be too complicated to install! (“pyt” for python and “er” for the R language).</p>

<p>I start Jupyter lab, dig a little and here is my first disappointment: no way to start a Julia kernel. I naively hoped that it would be installed as standard with Jupyter but nay.
Going back to the documentation to install the Julia kernel: you first have to install Julia on your computer.</p>

<h3 id="binary">Binary</h3>

<p>Nothing special here: I download <a href="https://julialang.org/downloads/">the installer</a> and it asks if I want to add Julia to my applications (I’m on Mac) so I accept.
A double click on the program launches a terminal with Julia.
However if I type <code class="language-plaintext highlighter-rouge">julia</code> in the terminal nothing happens so I add the path of the binary to my <code class="language-plaintext highlighter-rouge">PATH</code> in my .zshrc file.</p>

<h3 id="jupyter">Jupyter</h3>

<p>Now that I have the language, I need to install the kernel for Jupyter. Ultra easy, it is done in 2 command lines inside the <code class="language-plaintext highlighter-rouge">julia</code> terminal.</p>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="k">using</span> <span class="n">Pkg</span>
<span class="n">Pkg</span><span class="o">.</span><span class="n">add</span><span class="x">(</span><span class="s">"IJulia"</span><span class="x">)</span></code></pre></figure>

<p>This is a bit weird: there is nothing else to do, no confirmation action on jupyter to “accept” this new kernel.
So I nervously launch Jupyter and hurray, Julia notebooks are now available!</p>

<p>I quickly go through “Getting Started” to have at least the basic concepts in mind. I will be able to start coding while regularly consulting the documentation for this new language.</p>

<h2 id="the-julia-language-my-impressions">The Julia language: my impressions</h2>

<p>Small leap in time: we are now in January. I have tackled the daily challenges one after the other but we will not dwell on this as it is not the purpose of this article. Much to my disappointment, I haven’t completed them all yet as they seem to be more difficult than last year.</p>

<p>I won’t either present the language in details (there are tutorials for that). As a Python developer, I will give you my feelings about this language and a list of the main things that stood out to me:</p>

<ul>
  <li>Firstly, it is, as expected, quite close to Python. We perform simple tasks easily and the syntax is elegant.</li>
</ul>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="c"># Even if you are not a Julia programmer, I'm sure you can understand</span>
<span class="k">function</span><span class="nf"> part1</span><span class="x">(</span><span class="n">numbers</span><span class="x">)</span>
    <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">2</span><span class="o">:</span><span class="n">length</span><span class="x">(</span><span class="n">numbers</span><span class="x">)</span>
        <span class="k">if</span> <span class="n">numbers</span><span class="x">[</span><span class="n">i</span><span class="x">]</span> <span class="o">&gt;</span> <span class="n">numbers</span><span class="x">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="x">]</span>
            <span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>        
        <span class="k">end</span>
    <span class="k">end</span>
    <span class="k">return</span> <span class="n">counter</span>
<span class="k">end</span></code></pre></figure>

<ul>
  <li>The typing is present, strong but it can also be inferred. I think this is the best compromise. At work, I can make the types of the variables explicit. But when playing with code, I can skip it.
As a strong typing Python coder, I suffer a little every day: even if some efforts have been made, strong typing in Python doesn’t work so great.</li>
</ul>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="k">function</span><span class="nf"> part1</span><span class="x">(</span><span class="n">numbers</span><span class="o">::</span><span class="kt">Vector</span><span class="x">{</span><span class="kt">Int</span><span class="x">})</span><span class="o">::</span><span class="kt">Int</span>
    <span class="o">...</span>
<span class="k">end</span></code></pre></figure>

<ul>
  <li>The thing that mainly shocked me is the array indices starting at 1 instead of 0. As a result the last element of an interval is not excluded but included. A drastic choice but fair enough.</li>
</ul>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="n">items_except_first</span> <span class="o">=</span> <span class="n">items</span><span class="x">[</span><span class="mi">2</span><span class="o">:</span><span class="k">end</span><span class="x">]</span></code></pre></figure>

<ul>
  <li>The coding style is also weird: the names of functions and variables must be in lowercase without underscore so everything attached. I am not a personal fan.</li>
</ul>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="n">doyouthinkthisisagoodname</span> <span class="o">=</span> <span class="mi">42</span></code></pre></figure>

<ul>
  <li>The indentations are no longer strict (hallelujah) but out of habit (and concern for cleanliness) I still respect them.</li>
  <li>There is a line continuation by default. That also makes me very happy because it is another of my sufferings on python and its damn <code class="language-plaintext highlighter-rouge">\</code>.</li>
  <li>Say goodbye to the <code class="language-plaintext highlighter-rouge">:</code> at the beginning of python loops (yay).</li>
  <li>On the other hand, you have to use the <code class="language-plaintext highlighter-rouge">end</code> keyword to close each scope (function, if, for, …), what a shame, it makes the code much heavier.</li>
  <li>Very few imports, all useful functions are available by default.</li>
</ul>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="c"># No import required to read the content of a file</span>
<span class="n">lines</span> <span class="o">=</span> <span class="n">readlines</span><span class="x">(</span><span class="s">"input.txt"</span><span class="x">)</span></code></pre></figure>

<ul>
  <li>The mathematical unicode symbols are pleasant to read (≠, ∈, ÷, …).</li>
</ul>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="k">if</span> <span class="n">x</span> <span class="n">∈</span> <span class="n">valids</span> <span class="o">&amp;&amp;</span> <span class="n">x</span> <span class="o">≠</span> <span class="mi">0</span>
<span class="o">...</span>
<span class="k">end</span></code></pre></figure>

<ul>
  <li>It takes a little time to adapt to Matrices alias array in 2 dimensions. In particular the fact that they are built by columns and not by rows as is very often the case in algorithmic challenges.</li>
  <li>There is no class in Julia, only structures. These can contain attributes but no method. So we only have functions and never this or self.</li>
</ul>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="c"># mylist.push(element) doesn't work</span>
<span class="n">push!</span><span class="x">(</span><span class="n">mylist</span><span class="x">,</span> <span class="n">element</span><span class="x">)</span> <span class="c"># works</span></code></pre></figure>

<p>I also find this choice very strong, but I understand it. Don’t get me wrong, I’m a huge fan of object oriented programming and I use it a lot, even in Python. But sometimes I think about functions with two or more variables: should I code a function / method f which is used like <code class="language-plaintext highlighter-rouge">f(a, b)</code>, <code class="language-plaintext highlighter-rouge">a.f(b)</code> or <code class="language-plaintext highlighter-rouge">b.f(a)</code>? At least with Julia that question is settled.</p>
<ul>
  <li>The previous point introduces a fact that I did not suspect: Julia takes up many concepts of functional languages.
The syntax is very elegant when it comes to chaining function calls, I loved using the pipe <code class="language-plaintext highlighter-rouge">|&gt;</code> and the compose operator <code class="language-plaintext highlighter-rouge">∘</code>.</li>
</ul>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="c"># Compute the sum of integers between 1 and 10 and take the square root</span>
<span class="n">composing</span> <span class="o">=</span> <span class="x">(</span><span class="n">sqrt</span> <span class="n">∘</span> <span class="n">sum</span><span class="x">)(</span><span class="mi">1</span><span class="o">:</span><span class="mi">10</span><span class="x">)</span>
<span class="n">piping</span> <span class="o">=</span> <span class="mi">1</span><span class="o">:</span><span class="mi">10</span> <span class="o">|&gt;</span> <span class="n">sum</span> <span class="o">|&gt;</span> <span class="n">sqrt</span></code></pre></figure>

<p>On the other hand I remain a little frustrated not to have found a method to make currying / partial applications. With all the battery of beautiful things coming from the functional world, it would have been perfect to be able to do that simply. But maybe I haven’t looked well enough.</p>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="n">result</span> <span class="o">=</span> <span class="s">"42"</span> <span class="o">|&gt;</span> <span class="n">x</span> <span class="o">-&gt;</span> <span class="n">parse</span><span class="x">(</span><span class="kt">Int</span><span class="x">,</span> <span class="n">x</span><span class="x">)</span> <span class="c"># this works</span>
<span class="n">result</span> <span class="o">=</span> <span class="s">"42"</span> <span class="o">|&gt;</span> <span class="n">parse</span><span class="x">(</span><span class="kt">Int</span><span class="x">)</span> <span class="c"># sadly this doesn't work</span></code></pre></figure>

<ul>
  <li>I really liked the broadcasting operator which allows, by adding only a dot, to apply a function on an array of objects rather than on a single object. It also works on multi-dimensional arrays, very nice.</li>
</ul>

<figure class="highlight"><pre><code class="language-julia" data-lang="julia"><span class="n">f</span><span class="x">(</span><span class="n">x</span><span class="x">,</span><span class="n">y</span><span class="x">)</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">cos</span><span class="x">(</span><span class="n">y</span><span class="x">)</span>
<span class="n">numbers</span> <span class="o">=</span> <span class="x">[</span><span class="mi">0</span><span class="x">,</span> <span class="nb">pi</span><span class="x">,</span> <span class="mi">2</span><span class="o">*</span><span class="nb">pi</span><span class="x">]</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">map</span><span class="x">(</span><span class="n">z</span> <span class="o">-&gt;</span> <span class="n">f</span><span class="x">(</span><span class="mi">42</span><span class="x">,</span> <span class="n">z</span><span class="x">),</span> <span class="n">numbers</span><span class="x">)</span> <span class="c"># a = [43, 41, 43]</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="x">(</span><span class="mi">42</span><span class="x">,</span> <span class="n">numbers</span><span class="x">)</span> <span class="c"># same as previous line, b = [43, 41, 43]</span></code></pre></figure>

<p>In python, the NumPy library has also the concept of broadcasting. But it feels less like magic (and it’s more generic) in Julia.</p>

<ul>
  <li>There seems to be a lot of possibilities for doing other complex things like parallel programming or metaprogramming. I did not have the opportunity to use them during this challenge.</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Out of curiosity, I have checked what solutions the members of the Julia community shared on Github <a href="https://github.com/Bogdanp/awesome-advent-of-code#julia">awesome-advent-of-code</a>.
I was not disappointed, it has nothing to do with the level of code I have produced.
Even during the first days when the challenges are still simple, they use metaprogramming, obscure packages, they create modules and use complicated abstractions.</p>

<p>So, if you want to look cool when you code, you must:</p>
<ul>
  <li>never make a for loop.</li>
  <li>use functional programming as much as possible, if you can skip intermediate variables that’s perfect.</li>
  <li>make the code as short as possible using the language’s capabilities to the maximum and the different exotic packages available. Wait, it’s actually a real thing: <a href="https://codegolf.stackexchange.com/">codegolf.stackexchange.com</a></li>
  <li>make the code as incomprehensible as possible.</li>
</ul>

<p>I’m teasing a bit for the last few points, maybe I’m just jealous not to be as good as them in functional programming.</p>

<p>In any case, it was very pleasant to test the Julia language. It adapts very well to code puzzles. Unfortunately it is unlikely that it will dethrone the king Python. But I admit that I am impatiently awaiting the successor of Python: the language is a bit old and once you have tasted more recent languages (like Swift and Kotlin which I like a lot too), it’s hard to go back.</p>

<p>If you are curious, I have posted my solutions of the challenges on my <a href="https://github.com/borgeser/AdventOfCode">GitHub</a>. I do not guarantee that this code is the most efficient or that it uses 100% of the possibilities of the language but at least it is understandable by humans ;-)</p>]]></content><author><name>Roland Borgese</name></author><category term="Other" /><summary type="html"><![CDATA[Hi, I’m Roland, machine learning engineer at Fabernovel and this December I have decided to rediscover the AdventOfCode adventure. AdventOfCode has offered an advent calendar every year since 2015 but instead of receiving a piece of chocolate every day, we receive a riddle (separated into 2 parts) that must be solved by coding. At Fabernovel, several of us have participated and it’s nice to share with others the blockers and solutions day after day.]]></summary></entry><entry><title type="html">Manage legacy Objective-C in modern applications</title><link href="https://fabernovel.github.io/2022-01-14/manage-legacy-objc" rel="alternate" type="text/html" title="Manage legacy Objective-C in modern applications" /><published>2022-01-14T00:00:00+00:00</published><updated>2022-01-14T00:00:00+00:00</updated><id>https://fabernovel.github.io/2022-01-14/manage-legacy-objc</id><content type="html" xml:base="https://fabernovel.github.io/2022-01-14/manage-legacy-objc"><![CDATA[<p>Legacy or old projets might have Objective-C files, often mixed with Swift ones.
How to continue implementing new behavior and fixes in such a stack?</p>

<h2 id="context">Context</h2>

<p>In projects with both Swift and Objective-C, you have to expose one to the other. In order to do so, you have the Objective-C bridging header file <code class="language-plaintext highlighter-rouge">Project-Bridging-Header.h</code>. This allows you to expose the Objective-C files you declare in this file to Swift. For the other way around, you have the Swift bridging header file <code class="language-plaintext highlighter-rouge">Project-Swift.h</code>, that you must import in your Objective-C files. This bridging file is automatically generated with the <code class="language-plaintext highlighter-rouge">@objc</code> or <code class="language-plaintext highlighter-rouge">@objcMembers</code> annotations in Swift.</p>

<h2 id="objective-c-to-swift-golden-rule-never-write-more-than-one-line-of-objective-c">Objective-C to Swift golden rule: never write more than one line of Objective-C</h2>

<p>As languages evolve, pratices do as well. One main component of it is that people are now writing Swift code everyday, Objective-C one not so often. To be able to make some projet evolve the right way we have to be strict: never write more than one line of Objective-C code. To do that we have several options that you can see down below.</p>

<p>The key component is to write minimal Objective-C code to be able to bridge it with Swift files.</p>

<p>Given a regular Objective-C class like the following, let’s see what we can do.</p>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="k">@interface</span> <span class="nc">ViewController</span><span class="p">:</span> <span class="nc">UIViewController</span>
<span class="k">@end</span></code></pre></figure>

<h3 id="first-try-to-put-your-development-in-a-swift-extension">First try to put your development in a Swift extension</h3>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">extension</span> <span class="kt">ViewController</span> <span class="p">{}</span></code></pre></figure>

<p>This will be enough for most cases, the main limitation being that you can’t add new properties in this extension.</p>

<p>Example:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">extension</span> <span class="kt">ViewController</span> <span class="p">{</span>

    <span class="kd">@objc</span> <span class="kd">func</span> <span class="nf">functionInSwiftForObjc</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// Put your implementation here</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="k">@implementation</span> <span class="nc">ViewController</span><span class="p">:</span> <span class="nc">UIViewController</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">functionInObjc</span> <span class="p">{</span>
    <span class="p">[</span><span class="n">self</span> <span class="nf">functionInSwiftForObjc</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">@end</span></code></pre></figure>

<h3 id="then-use-heritage-to-add-properties">Then use heritage to add properties</h3>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="k">@interface</span> <span class="nc">ViewControllerObjc</span><span class="p">:</span> <span class="nc">UIViewController</span>
<span class="k">@end</span>

<span class="k">@implementation</span> <span class="nc">ViewControllerObjc</span><span class="p">:</span> <span class="nc">UIViewController</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">functionInObjc</span> <span class="p">{</span>
    <span class="c1">// some behavior</span>
    <span class="p">[(</span><span class="n">ViewController</span> <span class="o">*</span><span class="p">)</span><span class="n">self</span> <span class="nf">functionInSwiftForObjc</span><span class="p">];</span>
    <span class="c1">// some behavior</span>
<span class="p">}</span>
<span class="k">@end</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">ViewController</span><span class="p">:</span> <span class="kt">ViewControllerObjc</span> <span class="p">{</span>

    <span class="k">var</span> <span class="nv">newVariable</span><span class="p">:</span> <span class="kt">Class</span>
    
    <span class="kd">@objc</span> <span class="kd">func</span> <span class="nf">functionInSwiftForObjc</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// Put your implementation with newVariable here</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Tip: Rename the Objective-C file in NameObjc so the implementation outside this file will not change, and make a convention to not call NameObjc files directly in your project, they will only be used from their Swift child file.</p>

<h3 id="use-swift-components-in-objective-c-be-smart">Use Swift components in Objective-C: be smart</h3>

<h4 id="create-a-class-for-enum-that-are-not-visible-in-objective-c">Create a class for enum that are not visible in Objective-C</h4>

<p>If you use <a href="https://github.com/SwiftGen/SwiftGen">SwiftGen</a> to generate your wording, you can manually create an enum visible in Objective-C.</p>

<p>For instance, this code is auto-generated by SwiftGen:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">internal</span> <span class="kd">enum</span> <span class="kt">L10n</span> <span class="p">{</span>
  <span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">alertMessage</span> <span class="o">=</span> <span class="kt">L10n</span><span class="o">.</span><span class="nf">tr</span><span class="p">(</span><span class="s">"alert_message"</span><span class="p">)</span>
<span class="p">}</span></code></pre></figure>

<p>You can manually create a ObjcL10n class for variables which need to be Objective-C available</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">@objcMembers</span> <span class="kd">class</span> <span class="kt">ObjcL10n</span><span class="p">:</span> <span class="kt">NSObject</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">let</span> <span class="nv">alertMessage</span> <span class="o">=</span> <span class="kt">L10n</span><span class="o">.</span><span class="n">alertMessage</span>
<span class="p">}</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="k">@implementation</span> <span class="nc">ViewController</span><span class="p">:</span> <span class="nc">UIViewController</span>

<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">functionInObjc</span> <span class="p">{</span>
    <span class="c1">// some behavior</span>
    <span class="n">self</span><span class="p">.</span><span class="n">label</span><span class="p">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">ObjcL10n</span><span class="p">.</span><span class="n">alertMessage</span>
    <span class="c1">// some behavior</span>
<span class="p">}</span>
<span class="k">@end</span></code></pre></figure>

<h4 id="create-a-class-for-struct-that-are-not-visible-in-objective-c">Create a class for struct that are not visible in Objective-C</h4>

<p>Struct are not visible in Objective-C. In order to use them you can transform them into classes.</p>

<p>⚠️ Keep those classes simple as structs cannot be inherited and be aware that structs are value types while classes are reference ones.</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">ModelMapper</span> <span class="p">{</span>
    
    <span class="k">let</span> <span class="nv">parameter</span><span class="p">:</span> <span class="kt">Class</span>
    <span class="kd">func</span> <span class="nf">map</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Model</span> <span class="p">{}</span>
<span class="p">}</span></code></pre></figure>

<p>becomes:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">ModelMapper</span> <span class="p">{</span>

    <span class="kd">private</span> <span class="k">let</span> <span class="nv">parameter</span><span class="p">:</span> <span class="kt">Class</span>

    <span class="nf">init</span><span class="p">(</span><span class="nv">parameter</span><span class="p">:</span> <span class="kt">Class</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">parameter</span> <span class="o">=</span> <span class="n">parameter</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">map</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Model</span> <span class="p">{}</span>
<span class="p">}</span></code></pre></figure>

<h4 id="add-objc-c-bridges-for-swift-components-for-instance-swiftresult">Add Objc-c bridges for Swift components: for instance Swift.Result</h4>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">Repository</span> <span class="p">{</span>

    <span class="kd">func</span> <span class="nf">fetch</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Result</span><span class="o">&lt;</span><span class="kt">Class</span><span class="p">,</span> <span class="kt">Error</span><span class="o">&gt;</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="c1">// implementation</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>becomes for instance:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">Repository</span> <span class="p">{</span>

    <span class="kd">func</span> <span class="nf">fetch</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="kd">@escaping</span>  <span class="p">(</span><span class="kt">Class</span><span class="p">?,</span> <span class="kt">Error</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="c1">// same implementation</span>
        <span class="k">switch</span> <span class="n">result</span> <span class="p">{</span>
            <span class="k">case</span> <span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="k">let</span> <span class="nv">object</span><span class="p">):</span>
                <span class="nf">completion</span><span class="p">(</span><span class="n">object</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
            <span class="k">case</span> <span class="o">.</span><span class="nf">error</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span>
                <span class="nf">completion</span><span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h2 id="conclusion">Conclusion</h2>

<p>Objective-C has still a long run before its end-of-life support, so you can take your time and follow these easy steps to migrate your code, one class at a time. Keep in mind that you should not write more than one line of Objective-C and use some Swift bridges.</p>]]></content><author><name>Claire Peyron</name></author><category term="Swift" /><category term="iOS" /><category term="Objective-C" /><summary type="html"><![CDATA[Legacy or old projets might have Objective-C files, often mixed with Swift ones. How to continue implementing new behavior and fixes in such a stack?]]></summary></entry><entry><title type="html">SwiftArgumentParser: A Swift mod</title><link href="https://fabernovel.github.io/2021-09-13/swift-argument-parser-a-swift-mod" rel="alternate" type="text/html" title="SwiftArgumentParser: A Swift mod" /><published>2021-09-13T00:00:00+00:00</published><updated>2021-09-13T00:00:00+00:00</updated><id>https://fabernovel.github.io/2021-09-13/swift-argument-parser-a-swift-mod</id><content type="html" xml:base="https://fabernovel.github.io/2021-09-13/swift-argument-parser-a-swift-mod"><![CDATA[<p>During the WWDC 2021, Apple promoted for the first time its new <a href="https://github.com/apple/swift-argument-parser">SwiftArgumentParser</a> library. It aims to help creating command-line tools in Swift.</p>

<p>In just a few lines of code, we can build a fully documented command-line tool. Like in this example from its tiny (I’m pretty sure they made it tiny on purpose) <a href="https://github.com/apple/swift-argument-parser">README file</a>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">Repeat</span><span class="p">:</span> <span class="kt">ParsableCommand</span> <span class="p">{</span>

    <span class="kd">@Option</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="o">.</span><span class="n">shortAndLong</span><span class="p">,</span> <span class="nv">help</span><span class="p">:</span> <span class="s">"The number of times to repeat 'phrase'."</span><span class="p">)</span>
    <span class="k">var</span> <span class="nv">count</span><span class="p">:</span> <span class="kt">Int</span><span class="p">?</span>

    <span class="kd">@Argument</span><span class="p">(</span><span class="nv">help</span><span class="p">:</span> <span class="s">"The phrase to repeat."</span><span class="p">)</span>
    <span class="k">var</span> <span class="nv">phrase</span><span class="p">:</span> <span class="kt">String</span>

    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">repeatCount</span> <span class="o">=</span> <span class="n">count</span> <span class="p">??</span> <span class="o">.</span><span class="n">max</span>
        <span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">1</span><span class="o">...</span><span class="n">repeatCount</span> <span class="p">{</span>
          <span class="nf">print</span><span class="p">(</span><span class="n">phrase</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">Repeat</span><span class="o">.</span><span class="nf">main</span><span class="p">()</span></code></pre></figure>

<p>We express our CLI as a graceful command tree while <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> takes care of all the implementation details: it parses the command-line arguments, instantiates the target command, fulfills its arguments based on their names and finally runs it. It can even generate a built-in help description if a help option is passed:</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span>repeat <span class="nt">--help</span>
USAGE: repeat <span class="o">[</span><span class="nt">--count</span> &lt;count&gt;] &lt;phrase&gt;

ARGUMENTS:
  &lt;phrase&gt;                The phrase to repeat.

OPTIONS:
  <span class="nt">-c</span>, <span class="nt">--count</span> &lt;count&gt;     The number of <span class="nb">times </span>to repeat <span class="s1">'phrase'</span><span class="nb">.</span>
  <span class="nt">-h</span>, <span class="nt">--help</span>              Show <span class="nb">help </span><span class="k">for </span>this command.</code></pre></figure>

<p>Apple brands its library as <strong>straightforward</strong>.</p>

<p>This achievement is big: even if you have never used the library, chances are you can already tell what the code does and how you could use it to create your own CLI command out of it.</p>

<p>Have you ever seen anything as straightfordward in Swift?</p>

<p><code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> should remind you of <code class="language-plaintext highlighter-rouge">SwiftUI</code>. To create a new CLI option, you declare an <code class="language-plaintext highlighter-rouge">@Option</code> property without writing any parsing code. An approach that is similar to the <a href="https://developer.apple.com/xcode/swiftui">declarative syntax</a> of <code class="language-plaintext highlighter-rouge">SwiftUI</code>. In each case, thanks to this high level of abstraction, we only need to state what our code should do rather than writing it explicitly.</p>

<p>However, because Swift’s heavy focus on types, <code class="language-plaintext highlighter-rouge">straightfordwardness</code> is usually not the first quality that comes to mind. Contrary to dynamic languages, such as Ruby, Swift strongly relies on compilation-time validations, making it a safer, but significantly less flexible, language.</p>

<p>In fact, even after pondering on this for a bit, I have no idea how to implement such a behavior using Swift. <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> is not a regular Swift library.</p>

<p>To corroborate, in the previous example, if instead of the <code class="language-plaintext highlighter-rouge">main</code> method, we used the autogenerated empty initializer of <code class="language-plaintext highlighter-rouge">Repeat</code>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">command</span> <span class="o">=</span> <span class="kt">Repeat</span><span class="p">()</span>
<span class="n">command</span><span class="o">.</span><span class="nf">run</span><span class="p">()</span> <span class="c1">// fatal error</span></code></pre></figure>

<p>This state-of-the-art, Swift library would just… crash! (In this case, <code class="language-plaintext highlighter-rouge">Argument</code> was initialized with only a basic help description, how could <code class="language-plaintext highlighter-rouge">phrase</code> return a string out of it?)</p>

<p>It seems Apple has to bypass some Swift compiler safety checks to offer such an API. It decided to do it even if it could obviously lead to serious codebase issues. Something we, thorough Swift developers, are not used to doing when targeting a production environment. Something we are not used to seeing from Apple.</p>

<p>Starting from this point, I knew the <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> repository would contain some cryptic but powerful Swift usages. I had to deep dive inside it and figure out how Apple managed to make the magic happen.</p>

<h2 id="recreating-the-smallest-swiftargumentparser-api">Recreating the smallest SwiftArgumentParser API</h2>

<p>There are a lot of blogposts out there on how to use <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code>. In this one, I will instead highlight the main implementation tricks Apple used to provide such beautiful API and what is the cost of it.</p>

<p>To that purpose, I developed a basic version of the <a href="https://github.com/apple/swift-argument-parser">library</a> from scratch: <a href="https://github.com/gaetanzanella/FBArgumentParser">FBSwiftArgumentParser</a>.</p>

<p>The library only reproduces the following key features:</p>

<ul>
  <li>An <code class="language-plaintext highlighter-rouge">FBParsableCommand</code> protocol with a <code class="language-plaintext highlighter-rouge">main</code> entry point able to parse the command line arguments and execute the program’s logic.</li>
  <li>An <code class="language-plaintext highlighter-rouge">FBOption</code> String property wrapper to express the String options of the CLI. It supports default values.</li>
  <li>A builtin support for help.</li>
</ul>

<p>Similarly to the <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code>, an example of <code class="language-plaintext highlighter-rouge">FBArgumentParser</code> in a <code class="language-plaintext highlighter-rouge">README</code> file would look like this:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBRepeat</span><span class="p">:</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

    <span class="kd">@FBOption</span><span class="p">(</span><span class="nv">help</span><span class="p">:</span> <span class="s">"The phrase to repeat."</span><span class="p">)</span>
    <span class="k">var</span> <span class="nv">phrase</span> <span class="o">=</span> <span class="s">"Hello"</span> <span class="c1">// default value 😎</span>

    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">1</span><span class="o">...</span><span class="mi">2</span> <span class="p">{</span>
          <span class="nf">print</span><span class="p">(</span><span class="n">phrase</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">FBRepeat</span><span class="o">.</span><span class="nf">main</span><span class="p">()</span></code></pre></figure>

<p>And its usage like that:</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="o">&gt;</span> fbrepeat <span class="nt">--phrase</span> <span class="s2">"Bonjour"</span>
Bonjour
Bonjour

<span class="o">&gt;</span> fbrepeat
Hello
Hello

<span class="o">&gt;</span> fbrepeat <span class="nt">--help</span>
fbrepeat
<span class="nt">--phrase</span> <span class="o">(</span>optional<span class="o">)</span> <span class="o">(</span>default Hello<span class="o">)</span> The phrase to repeat.</code></pre></figure>

<p>Let’s see how to implement it step by step while keeping it straightforward.</p>

<h2 id="defining-the-main-fbparsablecommand-protocol">Defining the main <code class="language-plaintext highlighter-rouge">FBParsableCommand</code> protocol</h2>

<p>Of course, our implementation starts with the library’s main protocol: <code class="language-plaintext highlighter-rouge">FBParsableCommand</code>.</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>
  <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="k">throws</span>
<span class="p">}</span></code></pre></figure>

<p>It is a very basic version of the <code class="language-plaintext highlighter-rouge">ParsableCommand</code>. For example it does not support nesting commands nor contains those fancy properties to customize its help description. But that’s enough for now. As the original one, it will be used by the users of the library to declare their program logic.</p>

<p>We also define the same static <code class="language-plaintext highlighter-rouge">main</code> entry point in an extension (the users are not supposed to override it) as a static method:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">extension</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

  <span class="kd">static</span> <span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// TODO</span>
  <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>The execution steps of the <code class="language-plaintext highlighter-rouge">main</code> method are described in the <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code>’s <code class="language-plaintext highlighter-rouge">README</code> file:</p>

<blockquote>
  <p>You kick off execution by calling your type’s static main() method. The SwiftArgumentParser library parses the command-line arguments (#1), instantiates your command type (#2), and then either executes your run() method or exits with a useful message (#3).</p>
</blockquote>

<p>Let’s write those steps.</p>

<p>First, we create a <code class="language-plaintext highlighter-rouge">FBCommandParser</code> object to encapsulate the command line arguments parsing code.</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBCommandParser</span> <span class="p">{</span>

    <span class="k">let</span> <span class="nv">command</span><span class="p">:</span> <span class="kt">FBParsableCommand</span><span class="o">.</span><span class="k">Type</span>

    <span class="kd">func</span> <span class="nf">parse</span><span class="p">(</span><span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">])</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">arguments</span> <span class="o">=</span> <span class="nf">parseArguments</span><span class="p">(</span><span class="n">arguments</span><span class="p">)</span> <span class="c1">// # 1</span>
        <span class="c1">// TODO #2</span>
    <span class="p">}</span>

    <span class="c1">// creates a dict from the command line arguments:</span>
    <span class="c1">// `command --arg1 "value1" --arg2 "value2"` =&gt; ["arg1": "value1", "arg2": "value2"]</span>
    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">parseArguments</span><span class="p">(</span><span class="n">_</span> <span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">])</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">argumentsToParse</span> <span class="o">=</span> <span class="n">arguments</span>
        <span class="k">var</span> <span class="nv">result</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>
        <span class="k">while</span> <span class="n">argumentsToParse</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;=</span> <span class="mi">2</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">argument</span> <span class="o">=</span> <span class="n">argumentsToParse</span><span class="o">.</span><span class="nf">removeFirst</span><span class="p">()</span>
            <span class="k">let</span> <span class="nv">isAnArgument</span> <span class="o">=</span> <span class="n">argument</span><span class="o">.</span><span class="nf">starts</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="s">"-"</span><span class="p">)</span> <span class="o">||</span> <span class="n">argument</span><span class="o">.</span><span class="nf">starts</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="s">"--"</span><span class="p">)</span>
            <span class="k">if</span> <span class="o">!</span><span class="n">isAnArgument</span> <span class="p">{</span>
                <span class="k">throw</span> <span class="kt">ParsingError</span><span class="o">.</span><span class="n">invalidParameters</span>
            <span class="p">}</span>
            <span class="n">result</span><span class="p">[</span><span class="n">argument</span><span class="o">.</span><span class="nf">replacingOccurrences</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="s">"-"</span><span class="p">,</span> <span class="nv">with</span><span class="p">:</span> <span class="s">""</span><span class="p">)]</span> <span class="o">=</span> <span class="n">argumentsToParse</span><span class="o">.</span><span class="nf">removeFirst</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">result</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>As we only support CLI option type arguments (“–option value” or “-o value”), we store those key-value arguments in a basic dictionary. We would need something more sophisticated if more argument types were supported, but this will do for now.</p>

<p>Then, we execute the <code class="language-plaintext highlighter-rouge">run</code> method of the created instance and neatly exit the program if an error occured:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">extension</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

    <span class="kd">static</span> <span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">do</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">arguments</span> <span class="o">=</span> <span class="kt">CommandLine</span><span class="o">.</span><span class="n">arguments</span><span class="o">.</span><span class="nf">dropFirst</span><span class="p">()</span> <span class="c1">// we always ignore the first argument</span>
            <span class="k">var</span> <span class="nv">command</span> <span class="o">=</span> <span class="k">try</span> <span class="nf">parseAsRoot</span><span class="p">(</span><span class="kt">Array</span><span class="p">(</span><span class="n">arguments</span><span class="p">))</span> <span class="c1">// #1 #2</span>
            <span class="k">try</span> <span class="n">command</span><span class="o">.</span><span class="nf">run</span><span class="p">()</span> <span class="c1">// #3</span>
        <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
            <span class="nf">print</span><span class="p">(</span><span class="n">error</span><span class="o">.</span><span class="n">localizedDescription</span><span class="p">)</span> <span class="c1">// useful message in case of error</span>
            <span class="nf">exit</span><span class="p">(</span><span class="kt">EXIT_FAILURE</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">parseAsRoot</span><span class="p">(</span><span class="n">_</span> <span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">])</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">parser</span> <span class="o">=</span> <span class="kt">FBCommandParser</span><span class="p">(</span><span class="nv">command</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span>
        <span class="k">return</span> <span class="k">try</span> <span class="n">parser</span><span class="o">.</span><span class="nf">parse</span><span class="p">(</span><span class="nv">arguments</span><span class="p">:</span> <span class="n">arguments</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>We purposedly left a huge <code class="language-plaintext highlighter-rouge">TODO</code> in <code class="language-plaintext highlighter-rouge">FBCommandParser</code>. We basically still have to implement the core of the program: instantiating the target command and filling its properties based on the parsed CLI arguments dictionary.</p>

<h2 id="instantiating-client-commands">Instantiating client commands</h2>

<p><code class="language-plaintext highlighter-rouge">FBParsableCommand</code> obviously needs an initiliazer constraint: <code class="language-plaintext highlighter-rouge">main</code> is a static method and <code class="language-plaintext highlighter-rouge">run</code> an instance one.</p>

<p>Adding a basic <code class="language-plaintext highlighter-rouge">init(arguments: [String])</code> would work from an implementation perspective:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>
  <span class="nf">init</span><span class="p">(</span><span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">])</span>
<span class="p">}</span></code></pre></figure>

<p>But it would require a lot work for the library’s users to implement it. The user would have to manually fulfill its properties based on the given dictionary. Our library should do all the heavy lifting.</p>

<p>This is where <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> shined for the first time. It used a very clever technic:</p>

<blockquote>
  <p>It uses <code class="language-plaintext highlighter-rouge">Decodable</code> to make the compiler generate all the argument parsing code on the client side in one fell swoop</p>
</blockquote>

<p>In fact, if we consider the <code class="language-plaintext highlighter-rouge">[String: String]</code> argument array as a basic formatted data, we can consider <code class="language-plaintext highlighter-rouge">FBParsableCommand</code> as a custom data type. Thus if we can make the <code class="language-plaintext highlighter-rouge">FBParsableCommand</code> inherit from <code class="language-plaintext highlighter-rouge">Decodable</code>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">FBParsableCommand</span><span class="p">:</span> <span class="kt">Decoding</span> <span class="p">{}</span></code></pre></figure>

<p>All the decoding code can be generated at compilation time right inside the command implementation!</p>

<p>Let’s see it in action:</p>

<p>We first define a basic custom <code class="language-plaintext highlighter-rouge">Decoder</code> object able to return the value associated to the given argument property.</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBOptionsDecoder</span><span class="p">:</span> <span class="kt">Decoder</span> <span class="p">{</span>

  <span class="k">let</span> <span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span>

  <span class="c1">// MARK - Public</span>

  <span class="kd">func</span> <span class="nf">value</span><span class="p">(</span><span class="k">for</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
    <span class="n">arguments</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
  <span class="p">}</span>

  <span class="c1">// MARK - Decoder</span>

  <span class="c1">// ...</span>

  <span class="kd">func</span> <span class="n">container</span><span class="o">&lt;</span><span class="kt">Key</span><span class="o">&gt;</span><span class="p">(</span><span class="n">keyedBy</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">Key</span><span class="o">.</span><span class="k">Type</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">KeyedDecodingContainer</span><span class="o">&lt;</span><span class="kt">Key</span><span class="o">&gt;</span> <span class="k">where</span> <span class="kt">Key</span> <span class="p">:</span> <span class="kt">CodingKey</span> <span class="p">{</span>
      <span class="k">let</span> <span class="nv">container</span> <span class="o">=</span> <span class="kt">KeyeredArgumentContainer</span><span class="p">(</span><span class="nv">decoder</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span> <span class="nv">type</span><span class="p">:</span> <span class="n">type</span><span class="p">)</span>
      <span class="k">return</span> <span class="kt">KeyedDecodingContainer</span><span class="p">(</span><span class="n">container</span><span class="p">)</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="kt">KeyeredArgumentContainer</span><span class="o">&lt;</span><span class="kt">K</span><span class="p">:</span> <span class="kt">CodingKey</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">KeyedDecodingContainerProtocol</span> <span class="p">{</span>

    <span class="k">let</span> <span class="nv">decoder</span><span class="p">:</span> <span class="kt">FBOptionsDecoder</span>

    <span class="nf">init</span><span class="p">(</span><span class="nv">decoder</span><span class="p">:</span> <span class="kt">FBOptionsDecoder</span><span class="p">,</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">K</span><span class="o">.</span><span class="k">Type</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">decoder</span> <span class="o">=</span> <span class="n">decoder</span>
    <span class="p">}</span>

    <span class="c1">// ...</span>

    <span class="kd">func</span> <span class="n">decode</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="k">Type</span><span class="p">,</span> <span class="n">forKey</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">K</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">T</span> <span class="k">where</span> <span class="kt">T</span><span class="p">:</span> <span class="kt">Decodable</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="n">decoder</span><span class="o">.</span><span class="nf">value</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">key</span><span class="o">.</span><span class="n">stringValue</span><span class="p">)</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">element</span> <span class="o">=</span> <span class="n">value</span> <span class="k">as?</span> <span class="kt">T</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">element</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
          <span class="n">thow</span> <span class="kt">ParsingError</span><span class="o">.</span><span class="n">missingValue</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>It is almost straightforward as the value associated to a CLI option is already mapped to its name by our <code class="language-plaintext highlighter-rouge">FBCommandParser.parseArguments</code> method. We just need to use the decoding key as a search key in the parsed dictionary.</p>

<p>Then, we can use our custom <code class="language-plaintext highlighter-rouge">Decoder</code> to instanciate the target command, thanks to its conformance to <code class="language-plaintext highlighter-rouge">Decodable</code>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBCommandParser</span> <span class="p">{</span>

    <span class="kd">enum</span> <span class="kt">ParsingError</span><span class="p">:</span> <span class="kt">Error</span> <span class="p">{</span>
        <span class="k">case</span> <span class="n">invalidParameters</span>
    <span class="p">}</span>

    <span class="k">let</span> <span class="nv">command</span><span class="p">:</span> <span class="kt">FBParsableCommand</span><span class="o">.</span><span class="k">Type</span>

    <span class="kd">func</span> <span class="nf">parse</span><span class="p">(</span><span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">])</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">argumentDict</span> <span class="o">=</span> <span class="nf">parseArguments</span><span class="p">(</span><span class="n">arguments</span><span class="p">)</span>
        <span class="k">let</span> <span class="nv">decoder</span> <span class="o">=</span> <span class="kt">FBOptionsDecoder</span><span class="p">(</span><span class="nv">arguments</span><span class="p">:</span> <span class="n">argumentDict</span><span class="p">)</span>
        <span class="k">return</span> <span class="k">try</span> <span class="n">command</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">decoder</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="c1">// ...</span>
<span class="p">}</span></code></pre></figure>

<p>Now, as long as the client command properties implement <code class="language-plaintext highlighter-rouge">Decodable</code>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBRepeat</span><span class="p">:</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

    <span class="k">let</span> <span class="nv">phrase</span><span class="p">:</span> <span class="kt">String</span>

    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">1</span><span class="o">...</span><span class="mi">2</span> <span class="p">{</span>
          <span class="nf">print</span><span class="p">(</span><span class="n">phrase</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">FBRepeat</span><span class="o">.</span><span class="nf">main</span><span class="p">()</span></code></pre></figure>

<p>The Swift compiler will <a href="https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types">automatically</a> generate the appropriate <code class="language-plaintext highlighter-rouge">Decoder</code> method calls to fulfill all the properties of the target command.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="o">&gt;</span> fbrepeat <span class="nt">--phrase</span> <span class="s2">"Bonjour"</span>
Bonjour
Bonjour</code></pre></figure>

<p>Notice though that <code class="language-plaintext highlighter-rouge">FBRepeat</code> does not manage default values correctly yet. If we set a default value to a property:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBRepeat</span><span class="p">:</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

    <span class="k">var</span> <span class="nv">phrase</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"Hello"</span>

    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">1</span><span class="o">...</span><span class="mi">2</span> <span class="p">{</span>
          <span class="nf">print</span><span class="p">(</span><span class="n">phrase</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">FBRepeat</span><span class="o">.</span><span class="nf">main</span><span class="p">()</span></code></pre></figure>

<p>It will be ignored by the generated implementation of the <code class="language-plaintext highlighter-rouge">Decodable</code> protocol:</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="o">&gt;</span> fbrepeat
Decoding ERROR</code></pre></figure>

<p>It seems like a small problem but it would force  users to provide a custom implementation of <code class="language-plaintext highlighter-rouge">Decodable</code> in order to add default argument. We do not want that. We need to provide the users with an easy way to customize the decoding of its command.</p>

<p>You’ve guessed it, it is time to introduce property wrappers!</p>

<h2 id="using-property-wrappers-as-argument-descriptions">Using property wrappers as argument descriptions</h2>

<p>In <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> the information that we need to collect from the command line is defined using property wrappers. Each one of them matches to a type of argument (<code class="language-plaintext highlighter-rouge">@Flag</code>, <code class="language-plaintext highlighter-rouge">@Option</code>, <code class="language-plaintext highlighter-rouge">@Argument</code> etc) and provides initiliazers to describe them. We can specify how to parse the argument, its potential default value, its help description etc.</p>

<p>Let’s add a similar declarative API in <code class="language-plaintext highlighter-rouge">FBSwiftArgumentParser</code> and see how we can use it to tweak the parsing behavior.</p>

<p>We start with a basic <code class="language-plaintext highlighter-rouge">FBOption</code>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">@propertyWrapper</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">FBOption</span> <span class="p">{</span>

    <span class="kd">public</span> <span class="k">var</span> <span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">String</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">wrappedValue</span> <span class="o">=</span> <span class="s">""</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">wrappedValue</span> <span class="o">=</span> <span class="n">initialValue</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>It has two public initializers so clients can declare two kinds of arguments:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">FBOption.init(wrappedValue:)</code> describes an optional option with a default value</li>
  <li><code class="language-plaintext highlighter-rouge">FBOption.init()</code> describes a required option</li>
</ul>

<p>Its conformance to <code class="language-plaintext highlighter-rouge">Decodable</code> is straightforward, thanks to <a href="https://developer.apple.com/documentation/swift/decoder/2894882-singlevaluecontainer">singleValueContainer</a>, so we keep the previous benefits provided by the Swift compiler:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">extension</span> <span class="kt">FBOption</span><span class="p">:</span> <span class="kt">Decodable</span> <span class="p">{</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">from</span> <span class="nv">decoder</span><span class="p">:</span> <span class="kt">Decoder</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">container</span> <span class="o">=</span> <span class="k">try</span> <span class="n">decoder</span><span class="o">.</span><span class="nf">singleValueContainer</span><span class="p">()</span>
        <span class="n">wrappedValue</span> <span class="o">=</span> <span class="k">try</span> <span class="n">container</span><span class="o">.</span><span class="nf">decode</span><span class="p">(</span><span class="kt">String</span><span class="o">.</span><span class="k">self</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">FBRepeat</span><span class="p">:</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

    <span class="kd">@FBOption</span>
    <span class="k">var</span> <span class="nv">phrase</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"Hello"</span>

    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>As expected, our code is still as broken as before though:</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="o">&gt;</span> fbrepeat <span class="nt">--phrase</span> <span class="s2">"Bonjour"</span>
Bonjour
Bonjour

<span class="o">&gt;</span> fbrepeat
Decoding ERROR</code></pre></figure>

<p>When no value is specified, <code class="language-plaintext highlighter-rouge">FBCommand.main</code> method still tries to decode the command using <code class="language-plaintext highlighter-rouge">FBOption.init(from:)</code> without being able to provide a proper value for the <code class="language-plaintext highlighter-rouge">phrase</code> argument. The declared default “Hello” value is ignored.</p>

<p>Let’s adapt our parsing code and take into account the potential default values provided by the property wrappers.</p>

<h2 id="the-two-faces-of-the-command-property-wrappers">The two faces of the command property wrappers</h2>

<p>This is probably the most confusing aspect of the implementation.</p>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> property wrappers have two usages depending on their initiliazation</p>
</blockquote>

<p>Because of their declarative interfaces, when decoded, a property wrapper provides a value that can be used by the user during the execution of the command. Otherwise, it only provides a description of itself, used internally, that describes how to decode it.</p>

<p>Let’s highlight those two aspects in <code class="language-plaintext highlighter-rouge">FBOption</code>;</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">init()</code> and <code class="language-plaintext highlighter-rouge">init(wrappedValue:)</code>, used by the library’s clients, produces an <code class="language-plaintext highlighter-rouge">FBOption</code> that describes how to parse its underlying String value</li>
  <li><code class="language-plaintext highlighter-rouge">init(from:)</code>, used internally, produces a decoded <code class="language-plaintext highlighter-rouge">FBOption</code> with a valid wrapped value</li>
</ul>

<p>In code, we can represent the two cases using an enum:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBOptionDefinition</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">isOptional</span><span class="p">:</span> <span class="kt">Bool</span>
    <span class="k">let</span> <span class="nv">defaultValue</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span>
<span class="p">}</span>

<span class="kd">@propertyWrapper</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">FBOption</span> <span class="p">{</span>

    <span class="kd">enum</span> <span class="kt">State</span> <span class="p">{</span>
        <span class="k">case</span> <span class="nf">definition</span><span class="p">(</span><span class="kt">FBOptionDefinition</span><span class="p">)</span>
        <span class="k">case</span> <span class="nf">resolved</span><span class="p">(</span><span class="kt">String</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="k">let</span> <span class="nv">state</span><span class="p">:</span> <span class="kt">State</span>
<span class="p">}</span></code></pre></figure>

<p>In the first case, <code class="language-plaintext highlighter-rouge">FBOption</code> only wraps a <code class="language-plaintext highlighter-rouge">FBOptionDefinition</code>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">extension</span> <span class="kt">FBOption</span>  <span class="p">{</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">initialValue</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">state</span> <span class="o">=</span> <span class="o">.</span><span class="nf">definition</span><span class="p">(</span><span class="kt">FBOptionDefinition</span><span class="p">(</span><span class="nv">isOptional</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="n">defaultValue</span><span class="p">:</span> <span class="n">initialValue</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">state</span> <span class="o">=</span> <span class="o">.</span><span class="nf">definition</span><span class="p">(</span><span class="kt">FBOptionDefinition</span><span class="p">(</span><span class="nv">isOptional</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="n">defaultValue</span><span class="p">:</span> <span class="kc">nil</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>In the other case, <code class="language-plaintext highlighter-rouge">FBOption</code> stores its actual value:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">extension</span> <span class="kt">FBOption</span><span class="p">:</span> <span class="kt">Decodable</span> <span class="p">{</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">from</span> <span class="nv">decoder</span><span class="p">:</span> <span class="kt">Decoder</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">container</span> <span class="o">=</span> <span class="k">try</span> <span class="n">decoder</span><span class="o">.</span><span class="nf">singleValueContainer</span><span class="p">()</span>
        <span class="n">state</span> <span class="o">=</span> <span class="o">.</span><span class="nf">resolved</span><span class="p">(</span><span class="k">try</span> <span class="n">container</span><span class="o">.</span><span class="nf">decode</span><span class="p">(</span><span class="kt">String</span><span class="o">.</span><span class="k">self</span><span class="p">)))</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>As a result, <code class="language-plaintext highlighter-rouge">FBOption</code> can only return a value if it is decoded:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">FBOption</span> <span class="p">{</span>

    <span class="kd">public</span> <span class="k">var</span> <span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
        <span class="k">switch</span> <span class="n">state</span> <span class="p">{</span>
            <span class="k">case</span> <span class="o">.</span><span class="nv">definition</span><span class="p">:</span>
                <span class="nf">fatalError</span><span class="p">(</span><span class="s">"FBOption is not decoded"</span><span class="p">)</span>
            <span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">resolved</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
                <span class="k">return</span> <span class="n">value</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>This is the issue we faced in the introduction and obviously a tradeoff: <code class="language-plaintext highlighter-rouge">FBOption</code> has two usages but only one non optional public API.</p>

<p>However, as long as the library clients rely on the <code class="language-plaintext highlighter-rouge">main</code> method to execute their program, we can ensure only the autogenerated <code class="language-plaintext highlighter-rouge">Decodable.init(from:)</code> initializer of the parsable command will be used to call the <code class="language-plaintext highlighter-rouge">run</code> method. Thus each property wrappers will be decoded and return a value when the client tries to read a potential CLI option.</p>

<p>Furthermore, when a user declares a command, it now implicitly declares a list of <code class="language-plaintext highlighter-rouge">FBArgumentDefinition</code>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">Repeat</span><span class="p">:</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

    <span class="kd">@FBOption</span>
    <span class="k">var</span> <span class="nv">myUsualParameter1</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"Hello"</span> <span class="c1">// uses `FBOption.init(wrappedValue:)` thus it wraps an `FBArgumentDefinition`</span>

    <span class="kd">@FBOption</span>
    <span class="k">var</span> <span class="nv">myUsualParameter2</span><span class="p">:</span> <span class="kt">String</span> <span class="c1">// uses `FBOption.init()` thus it wraps an `FBArgumentDefinition`</span>
<span class="p">}</span></code></pre></figure>

<p>Let’s see how we can use them in our decoding code to ensure the optional values provided will be used when the decoding occurs.</p>

<h2 id="using-property-wrappers-as-reflective-markers">Using property wrappers as reflective markers</h2>

<p>Even if it seems at <a href="https://www.swiftbysundell.com/articles/reflection-in-swift/">odds</a> with its heavy focus on compile-time validation, Swift enables us to inspect, and work with, the members of a type — dynamically, at runtime. It is by using this uncommon capacity of Swift that Apple implemented the decoding of the property wrappers.</p>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> uses metaprogramming</p>
</blockquote>

<p>Let’s see how it works.</p>

<p>We first need a new constraint on the protocol:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">FBParsableCommand</span><span class="p">:</span> <span class="kt">Decodable</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span>
    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="k">throws</span>
<span class="p">}</span></code></pre></figure>

<p>This way, we force the user to provide a way to instantiate a command in a proper definition state, using the definition given by each property’s wrapper.</p>

<p>In fact, we could be confident that the users will only use descriptive wrapper initializers in this case:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">UsualRepeat</span><span class="p">:</span> <span class="kt">ParsableCommand</span> <span class="p">{</span>

    <span class="kd">@FBOption</span><span class="p">()</span>
    <span class="k">var</span> <span class="nv">usualProperty</span><span class="p">:</span> <span class="kt">Sting</span>
<span class="p">}</span></code></pre></figure>

<p>Seeing the use of a decoding one, even if it compiles, would be pretty unusual:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">decoder</span><span class="p">:</span> <span class="kt">Decoder</span> <span class="o">=</span> <span class="o">...</span>

<span class="kd">struct</span> <span class="kt">UnusualRepeat</span><span class="p">:</span> <span class="kt">ParsableCommand</span> <span class="p">{</span>

    <span class="kd">@FBOption</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">decoder</span><span class="p">)</span> <span class="c1">// compiles, but unexpected, and will crash</span>
    <span class="k">var</span> <span class="nv">unusualProperty</span><span class="p">:</span> <span class="kt">Sting</span>
<span class="p">}</span></code></pre></figure>

<p>Thanks to this assumption, as long as the user respects the contract, by instanciating a blank version of the target command, we can use <code class="language-plaintext highlighter-rouge">FBOption</code> as a reflective marker and retrieve all its argument definitions:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">private</span> <span class="kd">extension</span> <span class="kt">FBOption</span> <span class="p">{</span>

    <span class="kd">func</span> <span class="nf">definition</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">FBOptionDefinition</span> <span class="p">{</span>
        <span class="k">switch</span> <span class="n">state</span> <span class="p">{</span>
            <span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">definition</span><span class="p">(</span><span class="n">definition</span><span class="p">):</span>
                <span class="k">return</span> <span class="n">definition</span>
            <span class="k">case</span> <span class="o">.</span><span class="nv">resolved</span><span class="p">:</span>
                <span class="nf">fatalError</span><span class="p">(</span><span class="s">"FBOption is already decoded"</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">extension</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

    <span class="c1">// extracts all the argument definitions from the command type using mirroring</span>
    <span class="kd">static</span> <span class="kd">func</span> <span class="nf">argumentDefinitions</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="kt">FBOptionDefinition</span><span class="p">]</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">definitionByKey</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">FBOptionDefinition</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>
        <span class="k">let</span> <span class="nv">blankInstance</span> <span class="o">=</span> <span class="k">Self</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
        <span class="kt">Mirror</span><span class="p">(</span><span class="nv">reflecting</span><span class="p">:</span> <span class="n">blankInstance</span><span class="p">)</span><span class="o">.</span><span class="n">children</span><span class="o">.</span><span class="n">forEach</span> <span class="p">{</span> <span class="n">child</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">codingKey</span> <span class="o">=</span> <span class="n">child</span><span class="o">.</span><span class="n">label</span><span class="p">,</span> <span class="k">let</span> <span class="nv">definition</span> <span class="o">=</span> <span class="p">(</span><span class="n">child</span><span class="o">.</span><span class="n">value</span> <span class="k">as?</span> <span class="kt">FBOption</span><span class="p">)?</span><span class="o">.</span><span class="nf">definition</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="c1">// property wrappers are prefixed with "_"</span>
            <span class="k">let</span> <span class="nv">sanitizedCodingKey</span> <span class="o">=</span> <span class="kt">String</span><span class="p">(</span><span class="n">codingKey</span><span class="o">.</span><span class="n">first</span> <span class="o">==</span> <span class="s">"_"</span> <span class="p">?</span> <span class="n">codingKey</span><span class="o">.</span><span class="nf">dropFirst</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">:</span> <span class="n">codingKey</span><span class="o">.</span><span class="nf">dropFirst</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
            <span class="n">definitionByKey</span><span class="p">[</span><span class="n">sanitizedCodingKey</span><span class="p">]</span> <span class="o">=</span> <span class="n">definition</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">definitionByKey</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Notice that we need to parse the property wrapper names to match them with their corresponding key, each property wrapper starts with an underscore prefix.</p>

<p>We can now easily retrieve all the potential default values of a command with their corresponding arguments:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBCommandParser</span> <span class="p">{</span>

    <span class="k">let</span> <span class="nv">command</span><span class="p">:</span> <span class="kt">FBParsableCommand</span><span class="o">.</span><span class="k">Type</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">defaultParameters</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="kt">String</span><span class="p">]</span> <span class="p">{</span>
        <span class="n">command</span><span class="o">.</span><span class="nf">argumentDefinitions</span><span class="p">()</span><span class="o">.</span><span class="n">compactMapValues</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">defaultValue</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>By merging them with the initial CLI arguments dictionary, we can thus easily fill the potential gaps when initializing the target command, using this time its <code class="language-plaintext highlighter-rouge">Decoding.init(from:)</code> initializer:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBCommandParser</span> <span class="p">{</span>

    <span class="k">let</span> <span class="nv">command</span><span class="p">:</span> <span class="kt">FBParsableCommand</span><span class="o">.</span><span class="k">Type</span>

    <span class="kd">func</span> <span class="nf">parse</span><span class="p">(</span><span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">])</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">argumentDict</span> <span class="o">=</span> <span class="nf">parseArguments</span><span class="p">(</span><span class="n">arguments</span><span class="p">)</span>
        <span class="n">argumentDict</span><span class="o">.</span><span class="nf">merge</span><span class="p">(</span><span class="nf">defaultParameters</span><span class="p">())</span> <span class="p">{</span> <span class="n">left</span><span class="p">,</span> <span class="n">right</span> <span class="k">in</span> <span class="n">left</span> <span class="p">}</span> <span class="c1">// merging</span>
        <span class="k">let</span> <span class="nv">decoder</span> <span class="o">=</span> <span class="kt">FBOptionsDecoder</span><span class="p">(</span><span class="nv">arguments</span><span class="p">:</span> <span class="n">argumentDict</span><span class="p">)</span>
        <span class="k">return</span> <span class="k">try</span> <span class="n">command</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">decoder</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Thus when an optional option is used:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBRepeat</span><span class="p">:</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

    <span class="kd">@FBOption</span>
    <span class="k">var</span> <span class="nv">requiredPhrase</span><span class="p">:</span> <span class="kt">String</span>

    <span class="kd">@FBOption</span>
    <span class="k">var</span> <span class="nv">optionalPhrase</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"hello"</span>

    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">1</span><span class="o">...</span><span class="mi">2</span> <span class="p">{</span>
            <span class="nf">print</span><span class="p">(</span><span class="n">requiredPhrase</span><span class="p">)</span>
            <span class="nf">print</span><span class="p">(</span><span class="n">optionalPhrase</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">FBRepeat</span><span class="o">.</span><span class="nf">main</span><span class="p">()</span></code></pre></figure>

<p>The <code class="language-plaintext highlighter-rouge">@FBOption</code> wrapper will be decoded with its default value:</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="o">&gt;</span> fbrepeat <span class="nt">--requiredPhrase</span> bonjour
bonjour
hello
bonjour
hello</code></pre></figure>

<p>The decoding is fully functional!</p>

<p>Let’s implement the final feature: generating a help description when requested.</p>

<h2 id="generating-command-help">Generating command help</h2>

<p>Thanks to the mirror inspection already in place, it will be easy. We just have to add some final touches to our API.</p>

<p>We add a <code class="language-plaintext highlighter-rouge">FBCommandConfiguration</code> structure so the clients can provide a description of their commands:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">FBCommandConfiguration</span> <span class="p">{</span>

    <span class="kd">public</span> <span class="k">let</span> <span class="nv">usage</span><span class="p">:</span> <span class="kt">String</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">usage</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">usage</span> <span class="o">=</span> <span class="n">usage</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">FBParsableCommand</span><span class="p">:</span> <span class="kt">Decodable</span> <span class="p">{</span>

    <span class="kd">static</span> <span class="k">var</span> <span class="nv">configuration</span><span class="p">:</span> <span class="kt">FBParsableCommandConfiguration</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>

    <span class="nf">init</span><span class="p">()</span>
    <span class="kd">func</span> <span class="nf">run</span><span class="p">()</span> <span class="k">throws</span>
<span class="p">}</span>

<span class="kd">public</span> <span class="kd">extension</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>

    <span class="kd">static</span> <span class="k">var</span> <span class="nv">configuration</span><span class="p">:</span> <span class="kt">FBParsableCommandConfiguration</span> <span class="p">{</span>
        <span class="kt">FBParsableCommandConfiguration</span><span class="p">(</span>
            <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">(</span><span class="nv">describing</span><span class="p">:</span> <span class="k">Self</span><span class="o">.</span><span class="k">self</span><span class="p">)</span><span class="o">.</span><span class="nf">lowercased</span><span class="p">(),</span>
            <span class="nv">usage</span><span class="p">:</span> <span class="s">""</span>
        <span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>And a <code class="language-plaintext highlighter-rouge">help</code> parameter in each argument:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">FBOptionDefinition</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">isOptional</span><span class="p">:</span> <span class="kt">Bool</span>
    <span class="k">let</span> <span class="nv">defaultValue</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span>
    <span class="k">let</span> <span class="nv">help</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">FBOption</span> <span class="p">{</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">initialValue</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">help</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">state</span> <span class="o">=</span> <span class="o">.</span><span class="nf">definition</span><span class="p">(</span><span class="kt">FBOptionDefinition</span><span class="p">(</span><span class="nv">isOptional</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="n">defaultValue</span><span class="p">:</span> <span class="n">initialValue</span><span class="p">,</span> <span class="nv">help</span><span class="p">:</span> <span class="n">help</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">help</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">state</span> <span class="o">=</span> <span class="o">.</span><span class="nf">definition</span><span class="p">(</span><span class="kt">FBOptionDefinition</span><span class="p">(</span><span class="nv">isOptional</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="n">defaultValue</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">help</span><span class="p">:</span> <span class="n">help</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Based on the static definition of a command, we can now easily generate a detailed error anytime we catch a help argument:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">HelpError</span><span class="p">:</span> <span class="kt">LocalizedError</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">localizedDescription</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">HelpGenerator</span> <span class="p">{</span>

    <span class="k">let</span> <span class="nv">description</span><span class="p">:</span> <span class="kt">String</span>

    <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">command</span><span class="p">:</span> <span class="kt">FBParsableCommand</span><span class="o">.</span><span class="k">Type</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">description</span> <span class="o">=</span> <span class="s">""</span>
        <span class="n">description</span> <span class="o">+=</span> <span class="n">command</span><span class="o">.</span><span class="n">configuration</span><span class="o">.</span><span class="n">name</span>
        <span class="n">description</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
        <span class="k">if</span> <span class="o">!</span><span class="n">command</span><span class="o">.</span><span class="n">configuration</span><span class="o">.</span><span class="n">usage</span><span class="o">.</span><span class="n">isEmpty</span> <span class="p">{</span>
            <span class="n">description</span> <span class="o">+=</span> <span class="n">command</span><span class="o">.</span><span class="n">configuration</span><span class="o">.</span><span class="n">usage</span>
            <span class="n">description</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
        <span class="p">}</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">definition</span><span class="p">)</span> <span class="k">in</span> <span class="n">command</span><span class="o">.</span><span class="nf">argumentDefinitions</span><span class="p">()</span> <span class="p">{</span>
            <span class="n">description</span> <span class="o">+=</span> <span class="s">"--"</span> <span class="o">+</span> <span class="n">key</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nv">help</span> <span class="o">=</span> <span class="n">definition</span><span class="o">.</span><span class="n">help</span> <span class="p">{</span>
                <span class="n">description</span> <span class="o">+=</span> <span class="s">": "</span> <span class="o">+</span> <span class="n">help</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="n">definition</span><span class="o">.</span><span class="n">isOptional</span> <span class="p">{</span>
                <span class="n">description</span> <span class="o">+=</span> <span class="s">" (optional)"</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="n">definition</span><span class="o">.</span><span class="n">defaultValue</span> <span class="p">{</span>
                <span class="n">description</span> <span class="o">+=</span> <span class="s">" (default </span><span class="se">\(</span><span class="n">value</span><span class="se">)</span><span class="s">)"</span>
            <span class="p">}</span>
            <span class="n">description</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
        <span class="p">}</span>
        <span class="k">self</span><span class="o">.</span><span class="n">description</span> <span class="o">=</span> <span class="n">description</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">FBCommandParser</span> <span class="p">{</span>

    <span class="k">let</span> <span class="nv">command</span><span class="p">:</span> <span class="kt">FBParsableCommand</span><span class="o">.</span><span class="k">Type</span>

    <span class="kd">func</span> <span class="nf">parse</span><span class="p">(</span><span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">])</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">FBParsableCommand</span> <span class="p">{</span>
        <span class="k">try</span> <span class="nf">checkForHelp</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="n">arguments</span><span class="p">)</span>
        <span class="c1">// ...</span>
        <span class="k">return</span> <span class="k">try</span> <span class="n">command</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">decoder</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="c1">// ...</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">checkForHelp</span><span class="p">(</span><span class="k">in</span> <span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">])</span> <span class="k">throws</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">helpIndicators</span> <span class="o">=</span> <span class="p">[</span><span class="s">"-h"</span><span class="p">,</span> <span class="s">"--help"</span><span class="p">,</span> <span class="s">"help"</span><span class="p">]</span>
        <span class="k">let</span> <span class="nv">requestsHelp</span> <span class="o">=</span> <span class="n">helpIndicators</span><span class="o">.</span><span class="n">contains</span> <span class="p">{</span> <span class="n">indicator</span> <span class="k">in</span> <span class="n">arguments</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">indicator</span><span class="p">)</span> <span class="p">}</span>
        <span class="k">if</span> <span class="n">requestsHelp</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="kt">HelpError</span><span class="p">(</span><span class="nv">localizedDescription</span><span class="p">:</span> <span class="kt">HelpGenerator</span><span class="p">(</span><span class="n">command</span><span class="p">))</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>And here we go! <code class="language-plaintext highlighter-rouge">FBSwiftArgumentParser</code> is able to generate a description of our command when a help option is specified:</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="o">&gt;</span> fbrepeat <span class="nt">--help</span>
fbrepeat
<span class="nt">--optionalPhrase</span> <span class="o">(</span>optional<span class="o">)</span> <span class="o">(</span>default Hello<span class="o">)</span>
<span class="nt">--requiredPhrase</span></code></pre></figure>

<h2 id="the-drawback-of-the-swiftparserargument-implementation">The drawback of the SwiftParserArgument implementation</h2>

<p>Our basic version of <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> is small but it highlighted the main key tricks to create such a straightforward Swift library. Apple used a clever interlacing of operations executed at both compilation time and runtime.</p>

<p>At compilation time, all the command decoding code is generated on the client side thanks to the automatic conformance to <code class="language-plaintext highlighter-rouge">Decodable</code>. At runtime, <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> parses the static definition of each command and adapts the decoding strategy consequently. This last step is permitted by an extended usage of property wrappers: it allows Apple to encapsulate and inject logic right inside the client objects declaration. I really liked it.</p>

<p>However, to use the API of <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code>, clients have to follow an unusual list of agreements to ensure the program runs smoothly:</p>

<ul>
  <li>All the properties of a command have to be property wrappers provided by the library</li>
  <li>We must not override the default decoding behavior of the commands</li>
  <li>We must not instantiate commands ourself</li>
</ul>

<p>Such agreements can not be guaranteed by the Swift compiler. In fact, <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> counts <a href="https://github.com/apple/swift-argument-parser/search?q=fatalError">more than twenty fatalError</a> in its codebase. That is more than twenty manual checks that will lead to a crash if you are not respecting the usage contract.</p>

<p>That is why its implementation is hard to understand. In a regular codebase, targetting a production environment, we prefer to rely on the Swift compiler and therefore stay in the arguably safer environment it creates for us even if it means a more verbose code.</p>

<h2 id="a-swift-mod">A Swift mod</h2>

<p>With <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code>, Apple obviously made a tradeoff and decided that straightforwardness takes precedence over safety when writing CLI tools.</p>

<p>The fact that <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> will always be at the top of any programming stack surely influenced the decision.</p>

<p>It reminds me of <code class="language-plaintext highlighter-rouge">SwiftUI</code>. It has a beautiful Swift <a href="https://developer.apple.com/documentation/swiftui/">API</a> but it hides a lot of <a href="https://steipete.com/posts/state-of-swiftui/">unexpected behaviors</a>.</p>

<p>Like <code class="language-plaintext highlighter-rouge">SwiftUI</code>, <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> adds its own programming paradigm on top of Swift. By establishing rules that go way beyond traditional Swift library usage restrictions, like “commands should not use regular properties”, it extends the language itself, creating sort of a <a href="https://en.wikipedia.org/wiki/Video_game_modding">mod</a> of Swift and raising our code to new heights.</p>

<p>Of course, as the Swift compiler only knows Swift principles, those libraries have to validate their own rules at runtime with technics using metaprogramming as reflection. It inevitably leads to unsafe misuses.</p>

<p>In 2021, Apple had to <a href="https://developer.apple.com/videos/play/wwdc2021/10022/">demystify SwiftUI</a>. By revealing a bit of the SwiftUI implementation, Apple somehow agreed its usage of Swift can be confusing. We actually could not write such libraries using regular Swift - at least, using Swift as Apple wants us to use it, in a safe manner.</p>

<p>Still, chances are, those libraries are pioneers in the language features development rather than classic unsafe implementations. I will be not be suprised to see new Swift mods from Apple in the future.</p>

<p>As a side note, it is interesting to see how, after putting so much effort in building a safe language, Apple allows itself to bypass some of the compiler checks and write “flexible” Swift code, close to <a href="https://en.wikipedia.org/wiki/Objective-C#Dynamic_typing">the good old dynamic typing of Objective-C</a>.</p>

<h2 id="whats-next">What’s next?</h2>

<p>You can find all the code in a <a href="https://github.com/gaetanzanella/FBArgumentParser/tree/v1">dedicated repository</a> alongside a more <a href="https://github.com/gaetanzanella/FBArgumentParser/tree/v2">detailed version</a> of it.</p>

<p>I discovered <code class="language-plaintext highlighter-rouge">SwiftArgumentParser</code> when developing <a href="https://github.com/faberNovel/xcresource-cli">xcresource</a>. You can take a look at it! It aims to facilitate downloading Xcode templates or snippets from git repositories.</p>

<p>Thanks for reading!</p>]]></content><author><name>Gaétan Zanella</name></author><category term="Swift" /><summary type="html"><![CDATA[During the WWDC 2021, Apple promoted for the first time its new SwiftArgumentParser library. It aims to help creating command-line tools in Swift.]]></summary></entry><entry><title type="html">Enhance Xcode snippets using Git</title><link href="https://fabernovel.github.io/2021-07-22/enhance-xcode-snippets-using-git" rel="alternate" type="text/html" title="Enhance Xcode snippets using Git" /><published>2021-07-22T00:00:00+00:00</published><updated>2021-07-22T00:00:00+00:00</updated><id>https://fabernovel.github.io/2021-07-22/enhance-xcode-snippets-using-git</id><content type="html" xml:base="https://fabernovel.github.io/2021-07-22/enhance-xcode-snippets-using-git"><![CDATA[<p>In a previous <a href="https://fabernovel.github.io/2021-06-22/enhance-xcode-templates-using-git">post</a> we described how to combine <a href="https://github.com/faberNovel/xcresource-cli">xcresource</a> and <a href="https://www.hackingwithswift.com/articles/158/how-to-create-a-custom-xcode-template-for-coordinators">Xcode templates</a> to create a distributed sample code database available right inside Xcode.</p>

<p>This time, we will see how to use <a href="https://github.com/faberNovel/xcresource-cli">xcresource</a> to share and back Xcode codesnippets up!</p>

<h2 id="backing-codesnippets-up-in-a-git-repository">Backing codesnippets up in a git repository</h2>

<p><a href="https://sarunw.com/posts/how-to-create-code-snippets-in-xcode/#what-is-code-snippet%3F">Xcode snippets</a> are reusable blocks of code available in Xcode. You can browse them and create your own snippets in the <a href="https://sarunw.com/posts/how-to-create-code-snippets-in-xcode/#code-snippets-library">Xcode snippets library</a>.</p>

<p>Anytime you create a codesnippet, Xcode stores it in a local folder (<code class="language-plaintext highlighter-rouge">~/Library/Developer/Xcode/UserData/CodeSnippets</code>). You can use <code class="language-plaintext highlighter-rouge">xcresource snippet open</code> to browse them.</p>

<figure>
    <img src="/assets/enhance-xcode-snippets-using-git/local-snippets.png" height="300px" alt="xcresourcecodesnippets storage directory" />
    <figcaption>Xcode snippets directory</figcaption>
</figure>

<p>Thanks to <a href="https://github.com/faberNovel/xcresource-cli">xcresource</a>, you can move your snippets to a <a href="https://github.com/faberNovel/CodeSnippet_iOS/tree/master/XCSnippet">git repository</a> to back them up and install them again later locally. It is considered as a good pratice to rename them with explicit names.</p>

<p>Consider deleting your local snippets once copied, before installation, to avoid duplications:</p>

<figure class="highlight"><pre><code class="language-ssh" data-lang="ssh"><span class="err">&gt;</span> <span class="k">xcresource</span> snippet remove
<span class="err">&gt;</span> <span class="k">xcresource</span> snippet install --url url_to_git_repo --pointer branch_name</code></pre></figure>

<h2 id="installing-snippets-from-different-sources">Installing snippets from different sources</h2>

<p>When installing snippets, you can specify a <code class="language-plaintext highlighter-rouge">namespace</code> parameter:</p>

<figure class="highlight"><pre><code class="language-ssh" data-lang="ssh"><span class="err">&gt;</span> <span class="k">xcresource</span> snippet install --url url_to_git_repo --pointer branch_name --namespace my_namespace</code></pre></figure>

<p>A namespace acts as an installation folder. The snippets will be installed inside it. If the namespace already exists, it is replaced. Thus, you can use namespace to install snippets from different sources:</p>

<figure class="highlight"><pre><code class="language-ssh" data-lang="ssh"><span class="err">&gt;</span> <span class="k">xcresource</span> snippet install --url url_to_my_personal_git_repo --namespace personal_snippets

<span class="err">&gt;</span> <span class="k">xcresource</span> snippet install --url url_to_my_company_git_repo --namespace company_snippets

<span class="err">&gt;</span> <span class="k">xcresource</span> snippet list
<span class="c1"># personal_snippets</span>
<span class="err">-</span> <span class="k">personal</span> snippet <span class="m">1</span>
<span class="err">-</span> <span class="k">personal</span> snippet <span class="m">2</span>
<span class="c1"># company_snippets</span>
<span class="err">-</span> <span class="k">company</span> snippet <span class="m">1</span>
<span class="err">-</span> <span class="k">company</span> snippet <span class="m">2</span></code></pre></figure>

<h2 id="a-word-on-the-snippet-namespace-implementation">A word on the snippet namespace implementation</h2>

<p>When installing snippets, <code class="language-plaintext highlighter-rouge">xcresource</code> stores the namespace you specified in the summary of your snippets:</p>

<figure>
    <img src="/assets/enhance-xcode-snippets-using-git/namespace.png" height="300px" alt="The snippet namespace is stored in its summary" />
    <figcaption>The snippet namespace is stored in its summary</figcaption>
</figure>

<p>Be careful to not override it when editing your snippets using Xcode.</p>

<h2 id="whats-next">What’s next?</h2>

<p>Try to create your own Xcode snippets repository and display them in Xcode of course!</p>

<p>Discover how you can use <a href="https://github.com/faberNovel/xcresource-cli">xcresource</a> to <a href="https://fabernovel.github.io/2021-06-22/enhance-xcode-templates-using-git">enhance your Xcode templates</a> ;)</p>]]></content><author><name>Gaétan Zanella</name></author><category term="iOS" /><category term="macOS" /><summary type="html"><![CDATA[In a previous post we described how to combine xcresource and Xcode templates to create a distributed sample code database available right inside Xcode.]]></summary></entry><entry><title type="html">Dealing with Apple 2FA for iOS automated app delivery</title><link href="https://fabernovel.github.io/2021-07-20/dealing-with-apple-2FA" rel="alternate" type="text/html" title="Dealing with Apple 2FA for iOS automated app delivery" /><published>2021-07-20T00:00:00+00:00</published><updated>2021-07-20T00:00:00+00:00</updated><id>https://fabernovel.github.io/2021-07-20/dealing-with-apple-2FA</id><content type="html" xml:base="https://fabernovel.github.io/2021-07-20/dealing-with-apple-2FA"><![CDATA[<p>Starting in February 2021, Apple added an additional layer of security by making it mandatory to use the two-factor authentication or two-step authentication system when signing users into the App Store Connect.</p>

<p>Although the two-factor authentication is a great asset for account security, the fact that it was made mandatory has caused some trouble on our CI/CD pipeline. In this article we’ll see how we can overcome those issues.</p>

<h2 id="using-the-new-api">Using the new API</h2>

<p>At WWDC18, Apple announced an API for App Store Connect automation. It provides a familiar REST API designed to facilitate the automation of many tasks that we would normally do manually on the website or using fastlane.</p>

<p>Historically, fastlane has been using Apple ID with username and password to log into the Apple Developer Portal and App Store Connect so it can update the certificates, mobile provisioning profiles, upload app binaries, etc. This method worked quite well throughout the years. But since fastlane shipped a <a href="https://github.com/fastlane/fastlane/releases/tag/2.157.0">new version</a> that leverages the new Apple API, it is recommended to use the latter when we are able to.</p>

<p>Fastlane will continue to use these two ways of authentication since there are benefits and but still some downsides when using the new API.</p>

<p>Pros:</p>
<ul>
  <li>No 2FA needed</li>
  <li>Better performance</li>
  <li>Documented API</li>
  <li>Reliable, stable, and recommended by Apple</li>
</ul>

<p>Cons:</p>
<ul>
  <li>Cannot be used with Enterprise accounts</li>
  <li>There is no way to download dSYM files</li>
</ul>

<p>If you are not affected by these downsides, migrating to App Store Connect API is a way to solve the 2FA problem.
To do this, the Apple account holder needs to generate the key from appstore connect and add it the CI as an environnement variable, for exemple: <code class="language-plaintext highlighter-rouge">APP_STORE_CONNECT_API_KEY_CONTENT</code>.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">app_store_connect_api_key</span><span class="p">(</span>
    <span class="ss">key_id: </span><span class="no">ENV</span><span class="p">[</span><span class="s2">"APP_STORE_CONNECT_API_KEY_ID"</span><span class="p">],</span>
    <span class="ss">issuer_id: </span><span class="no">ENV</span><span class="p">[</span><span class="s2">"APP_STORE_CONNECT_API_ISSUER_ID"</span><span class="p">],</span>
    <span class="ss">key_content: </span><span class="s2">"</span><span class="si">#{</span><span class="no">ENV</span><span class="p">[</span><span class="s2">"APP_STORE_CONNECT_API_KEY_CONTENT"</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s1">'\n'</span><span class="p">,</span> <span class="s1">'\\n'</span><span class="p">),</span>
    <span class="ss">in_house: </span><span class="kp">false</span><span class="p">,</span>
<span class="p">)</span></code></pre></figure>

<p>The <code class="language-plaintext highlighter-rouge">app_store_connect_api_key</code> action sets the API key value into the global lane context dictionary and actions like <code class="language-plaintext highlighter-rouge">pilot</code> will automatically load the API Key from the <code class="language-plaintext highlighter-rouge">lane_context</code> dictionary.</p>

<h2 id="using-apple-id">Using Apple ID:</h2>

<p>For Enterprise accounts, using Apple ID with fastlane is the only way to go.</p>

<p>Fastlane has recently added an option to opt-in to skipping 2FA upgrade with the <code class="language-plaintext highlighter-rouge">SPACESHIP_SKIP_2FA_UPGRADE=1</code> environnement variable. This feature is available on version <a href="https://github.com/fastlane/fastlane/releases/tag/2.173.0">2.173.0</a> and works only for accounts that still has not enabled the 2FA. It’s a short term solution before 2FA is fully forced by Apple, and can be used to buy time while implementing a more sustainable solution.</p>

<p>At Fabernovel, we have a main Apple account that manages certificates on multiple teams. It is used by our CI/CD system and project managers / developers to access the developer Portal and App Store connect.</p>

<p>With 2FA enabled, we have to ensure that this account is accessible by both parties without much trouble logging-in.</p>

<p>The idea is to use fastlane’s <em>spaceauth</em> command line to generate a <code class="language-plaintext highlighter-rouge">FASTLANE_SESSION</code> (cookie) and add it as an environment variable of our CI system. This session will be reused instead of triggering a new login each time fastlane communicates with Apple’s APIs.</p>

<p>Under the hood, <em>spaceauth</em> is really just making HTTP requests to App Store connect with the provided username and password. It needs the 2FA authentication code to be able to log in and retrieve the cookie.</p>

<p>We needed a way to automate the retrieval of the 2FA code and post it to our Slack so that people can use it whenever they try to log-in. And also when using <em>spaceauth</em> to generate the <em>fastlane session</em> each month (or so we thought). So we tried different leads.</p>

<h2 id="2fa-code-retrieval-with-twilio">2FA code retrieval with Twilio:</h2>

<h3 id="using-sms">Using SMS:</h3>

<p>Our first plan was to use a Twilio number as trusted phone on our Apple account, then retrieve the SMS containing the 2FA code using a webhook, and send it to slack. Sounds easy right ?</p>

<p>Unfortunately, we were never able to receive any 2FA code using a Twilio number. After a little digging it turns out the problem was that Apple uses “short code numbers” to send 2FA codes, and receiving SMS from these kind of numbers is not a feature that is enabled by default on Twilio.</p>

<p>After getting in touch with the support to enable communication with short numbers, we found out that this feature is not fully supported and has multiple restrictions. Short Numbers can only be used on to receive SMS from the same country, and even then, Twilio does not guarantee that all SMSs will be received. Using their numbers for 2FA codes is not something that they recommend :</p>

<blockquote>
  <p>These codes are supposed to be sent to mobile devices. Apple may intentionally not route them to Twilio, or it may suddenly stop working one day, so it is not recommended to use Twilio numbers for this use.</p>
</blockquote>

<h3 id="using-a-phone-call">Using a phone call</h3>

<p>Another quick attempt that we tried was to use a phone call instead of SMS as we noticed that Apple does not use short numbers in this case. We used the “record and transcribe” feature provided by Twilio but with no luck. Apple hangs out as soon as we begin to record the call.</p>

<p>By then, it was clear that using Twilio wasn’t really convenient for our use case.</p>

<h2 id="using-our-own-ci-machine">Using our own CI machine.</h2>

<p>Besides using a trusted phone number to receive the 2FA code, we can also use a trusted macOS device that displays the 2FA code when we try to log in.</p>
<figure>
  <img src="../assets/dealing-with-apple-2FA/apple-id-two-factor-auth-code.jpg" alt="2FA window" />
  <figcaption>macOS 2FA code window</figcaption>
</figure>
<p>We had this crazy idea of retrieving the code using the automation scripting language Applescript that worked quite well surprisingly.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#! /usr/bin/env osascript</span>
<span class="n">set</span> <span class="n">repeat_delay</span> <span class="n">to</span> <span class="mi">60</span>
<span class="n">repeat</span>
  <span class="n">tell</span> <span class="n">application</span> <span class="s2">"System Events"</span>
    <span class="k">if</span> <span class="nb">name</span> <span class="n">of</span> <span class="n">every</span> <span class="n">process</span> <span class="n">contains</span> <span class="s2">"FollowUpUI"</span> <span class="k">then</span>
      <span class="n">tell</span> <span class="n">window</span> <span class="mi">1</span> <span class="n">of</span> <span class="n">process</span> <span class="s2">"FollowUpUI"</span>
        <span class="n">set</span> <span class="n">allow_button</span> <span class="n">to</span> <span class="n">a</span> <span class="n">reference</span> <span class="n">to</span> <span class="p">(</span><span class="n">first</span> <span class="n">button</span> <span class="n">whose</span> <span class="nb">name</span> <span class="n">is</span> <span class="s2">"Allow"</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">allow_button</span> <span class="n">exists</span> <span class="k">then</span>
          <span class="n">log</span> <span class="p">(</span><span class="s2">"Allow 2FA request"</span><span class="p">)</span>
          <span class="n">click</span> <span class="n">allow_button</span>
          <span class="n">delay</span> <span class="mi">2</span>
        <span class="k">else</span>
          <span class="n">log</span> <span class="p">(</span><span class="s2">"Skipping 'Authorize' step, button cannot be found."</span><span class="p">)</span>
        <span class="k">end</span> <span class="k">if</span>
        <span class="n">set</span> <span class="n">code_static_text</span> <span class="n">to</span> <span class="n">a</span> <span class="n">reference</span> <span class="n">to</span> <span class="p">(</span><span class="n">static</span> <span class="n">text</span> <span class="mi">1</span> <span class="n">of</span> <span class="n">group</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">code_static_text</span> <span class="n">exists</span> <span class="k">then</span>
          <span class="n">set</span> <span class="n">code</span> <span class="n">to</span> <span class="n">value</span> <span class="n">of</span> <span class="n">code_static_text</span>
          <span class="n">log</span> <span class="p">(</span><span class="s2">"2FA Code : "</span> <span class="o">&amp;</span> <span class="n">code</span><span class="p">)</span>
          <span class="c1"># curl to slack channel with code</span>
          <span class="n">log</span> <span class="p">(</span><span class="s2">"Send code to Slack channel..."</span><span class="p">)</span>
          <span class="n">set</span> <span class="n">slack_webhook</span> <span class="n">to</span> <span class="s2">"&lt;slack_channel_url&gt;"</span>
          <span class="n">set</span> <span class="n">post_data</span> <span class="n">to</span> <span class="s2">"{</span><span class="se">\"</span><span class="s2">text</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">2FA: "</span> <span class="o">&amp;</span> <span class="n">code</span> <span class="o">&amp;</span> <span class="s2">"</span><span class="se">\"</span><span class="s2">}"</span>
          <span class="n">set</span> <span class="n">curl_command</span> <span class="n">to</span> <span class="s2">"curl -X POST -H 'Content-Type: application/json' --data '"</span> <span class="o">&amp;</span> <span class="n">post_data</span> <span class="o">&amp;</span> <span class="s2">"' "</span> <span class="o">&amp;</span> <span class="n">slack_webhook</span>
          <span class="k">do</span> <span class="n">shell</span> <span class="n">script</span> <span class="n">curl_command</span>
          <span class="n">log</span> <span class="p">(</span><span class="s2">"Done ! Close window"</span><span class="p">)</span>
          <span class="n">click</span> <span class="n">button</span> <span class="s2">"Done"</span>
        <span class="k">end</span> <span class="k">if</span>
      <span class="k">end</span> <span class="n">tell</span>
    <span class="k">else</span>
      <span class="n">log</span> <span class="p">(</span><span class="s2">"Couldn't find 2FA window"</span><span class="p">)</span>
    <span class="k">end</span> <span class="k">if</span>
  <span class="k">end</span> <span class="n">tell</span>
  <span class="n">log</span> <span class="p">(</span><span class="s2">"Relaunch script in "</span> <span class="o">&amp;</span> <span class="n">repeat_delay</span> <span class="o">&amp;</span> <span class="s2">" seconds"</span><span class="p">)</span>
  <span class="n">delay</span> <span class="n">repeat_delay</span>
<span class="k">end</span> <span class="n">repeat</span></code></pre></figure>

<p>This script runs on one of our CI machines every minute to check if there is any 2FA UI open, then retrieve the text code and send it to slack directly so that anyone trying to connect may receive the code on a dedicated channel.</p>

<p>We later realized, after some tests, that the <code class="language-plaintext highlighter-rouge">FASTLANE_SESSION</code> provided by <em>spaceauth</em> only lasts for eight hours. So manually interacting with <em>spaceauth</em> and updating the environment variable on our CI wasn’t an option anymore, we needed a way to automate this process.</p>

<p><em>Spaceauth</em> can only be used with interactive-mode. Therefore, we created a ruby script that is scheduled multiple times a day to interacts with it using the <code class="language-plaintext highlighter-rouge">ruby_expect</code> library so we can get and update the session.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">require</span> <span class="s2">"ruby_expect"</span>

<span class="k">class</span> <span class="nc">FastlaneSessionExtractor</span>
  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">execute_process</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
    <span class="n">r</span><span class="p">,</span> <span class="n">w</span> <span class="o">=</span> <span class="no">IO</span><span class="p">.</span><span class="nf">pipe</span>
    <span class="no">Process</span><span class="p">.</span><span class="nf">spawn</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="ss">:out</span> <span class="o">=&gt;</span> <span class="n">w</span><span class="p">,</span> <span class="ss">:err</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="ss">:child</span><span class="p">,</span> <span class="ss">:out</span><span class="p">])</span>
    <span class="n">pid</span><span class="p">,</span> <span class="n">status</span> <span class="o">=</span> <span class="no">Process</span><span class="p">.</span><span class="nf">wait2</span>
    <span class="n">w</span><span class="p">.</span><span class="nf">close</span>
    <span class="n">output</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="nf">read</span>
    <span class="n">r</span><span class="p">.</span><span class="nf">close</span>
    <span class="n">output</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">get_fastlane_session</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">password</span><span class="p">)</span>
    <span class="n">session</span> <span class="o">=</span> <span class="s1">'none'</span>
    <span class="n">exp</span> <span class="o">=</span> <span class="no">RubyExpect</span><span class="o">::</span><span class="no">Expect</span><span class="p">.</span><span class="nf">spawn</span><span class="p">(</span><span class="s2">"FASTLANE_USER=</span><span class="se">\"</span><span class="si">#{</span><span class="n">user</span><span class="si">}</span><span class="se">\"</span><span class="s2"> FASTLANE_PASSWORD=</span><span class="se">\"</span><span class="si">#{</span><span class="n">password</span><span class="si">}</span><span class="se">\"</span><span class="s2"> bundle exec fastlane spaceauth"</span><span class="p">,</span> <span class="p">{</span> <span class="ss">debug: </span><span class="kp">true</span> <span class="p">})</span>
    <span class="n">exp</span><span class="p">.</span><span class="nf">timeout</span> <span class="o">=</span> <span class="mi">10</span>
    <span class="c1"># exp.interact # useful for debugging</span>
    <span class="n">exp</span><span class="p">.</span><span class="nf">procedure</span> <span class="k">do</span>
      <span class="n">received_input_index</span> <span class="o">=</span> <span class="mi">0</span>
      <span class="k">while</span> <span class="o">!</span><span class="n">received_input_index</span><span class="p">.</span><span class="nf">nil?</span> <span class="o">&amp;&amp;</span> <span class="n">received_input_index</span> <span class="o">!=</span> <span class="mi">3</span>
        <span class="no">STDERR</span><span class="p">.</span><span class="nf">puts</span> <span class="s2">"Handle new expectation from spaceauth"</span>
        <span class="n">received_input_index</span> <span class="o">=</span> <span class="n">any</span> <span class="k">do</span>
          <span class="n">expect</span><span class="p">(</span><span class="sr">/(.*The login credentials for .* seem to be wrong.*|.*Could not find a matching phone number.*)/</span><span class="p">)</span> <span class="k">do</span>
            <span class="no">STDERR</span><span class="p">.</span><span class="nf">puts</span> <span class="s2">"Error while logging in:"</span>
            <span class="no">STDERR</span><span class="p">.</span><span class="nf">puts</span> <span class="n">last_match</span>
            <span class="nb">exit</span> <span class="mi">1</span>
          <span class="k">end</span>
          <span class="n">expect</span><span class="p">(</span><span class="sr">/.*Please enter the 6 digit code.*/</span><span class="p">)</span> <span class="k">do</span>
            <span class="no">STDERR</span><span class="p">.</span><span class="nf">puts</span> <span class="s2">"Obtain 2FA code from UI"</span>
            <span class="n">output</span> <span class="o">=</span> <span class="no">FastlaneSessionExtractor</span><span class="p">.</span><span class="nf">execute_process</span><span class="p">(</span><span class="s2">"bash -c '</span><span class="si">#{</span><span class="n">__dir__</span><span class="si">}</span><span class="s2">/apple_2fa_interceptor.applescript'"</span><span class="p">)</span>
            <span class="n">code</span> <span class="o">=</span> <span class="n">output</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="sr">/\s+/</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span>
            <span class="no">STDERR</span><span class="p">.</span><span class="nf">puts</span> <span class="n">code</span>
            <span class="nb">send</span> <span class="n">code</span>
          <span class="k">end</span>
          <span class="n">expect</span><span class="p">(</span><span class="sr">/.*Should fastlane copy the cookie into your clipboard, so you can easily paste it.*/</span><span class="p">)</span> <span class="k">do</span>
            <span class="no">STDERR</span><span class="p">.</span><span class="nf">puts</span> <span class="s2">"Copy session"</span>
            <span class="nb">send</span> <span class="s1">'y'</span>
          <span class="k">end</span>
          <span class="n">expect</span><span class="p">(</span><span class="sr">/.*Successfully copied text into your clipboard.*/</span><span class="p">)</span> <span class="k">do</span>
            <span class="no">STDERR</span><span class="p">.</span><span class="nf">puts</span> <span class="s2">"Spaceauth finished obtaining session !"</span>
            <span class="n">session</span> <span class="o">=</span> <span class="no">FastlaneSessionExtractor</span><span class="p">.</span><span class="nf">execute_process</span><span class="p">(</span><span class="s2">"pbpaste"</span><span class="p">)</span>
            <span class="no">FastlaneSessionExtractor</span><span class="p">.</span><span class="nf">execute_process</span><span class="p">(</span><span class="s2">"bash -c 'echo -n </span><span class="se">\"\"</span><span class="s2"> | pbcopy'"</span><span class="p">)</span>
          <span class="k">end</span>
        <span class="k">end</span>
      <span class="k">end</span>
    <span class="k">end</span>
    <span class="n">session</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>After the session retrieval we just had to update the new value as an organization secret using the Github API and voila.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># This assumes valid credentials are filled on a config file.</span>
<span class="n">session</span> <span class="o">=</span> <span class="no">FastlaneSessionExtractor</span><span class="p">.</span><span class="nf">get_fastlane_session</span><span class="p">(</span><span class="no">Config</span><span class="o">::</span><span class="no">APPLE_IDENTIFIERS</span><span class="p">[</span><span class="ss">:email</span><span class="p">],</span> <span class="no">Config</span><span class="o">::</span><span class="no">APPLE_IDENTIFIERS</span><span class="p">[</span><span class="ss">:password</span><span class="p">])</span>
<span class="n">github_api_token</span> <span class="o">=</span> <span class="no">GithubActionsAPI</span><span class="p">.</span><span class="nf">generate_github_api_token</span><span class="p">(</span>
    <span class="no">Config</span><span class="o">::</span><span class="no">GITHUB</span><span class="p">[</span><span class="ss">:identifiers</span><span class="p">][</span><span class="ss">:private_key</span><span class="p">],</span>
    <span class="no">Config</span><span class="o">::</span><span class="no">GITHUB</span><span class="p">[</span><span class="ss">:identifiers</span><span class="p">][</span><span class="ss">:app_id</span><span class="p">],</span>
    <span class="no">Config</span><span class="o">::</span><span class="no">GITHUB</span><span class="p">[</span><span class="ss">:identifiers</span><span class="p">][</span><span class="ss">:installation_id</span><span class="p">]</span>
<span class="p">)</span>
<span class="n">public_key_data</span> <span class="o">=</span> <span class="no">GithubActionsAPI</span><span class="p">.</span><span class="nf">get_secrets_public_key_data</span><span class="p">(</span><span class="n">github_api_token</span><span class="p">,</span> <span class="no">Config</span><span class="o">::</span><span class="no">GITHUB</span><span class="p">[</span><span class="ss">:scope</span><span class="p">])</span>
<span class="n">encrypted_data</span> <span class="o">=</span> <span class="no">GithubActionsAPI</span><span class="p">.</span><span class="nf">encrypt_value</span><span class="p">(</span><span class="n">public_key_data</span><span class="p">[</span><span class="s1">'key'</span><span class="p">],</span> <span class="n">session</span><span class="p">)</span>
<span class="n">update_response</span> <span class="o">=</span> <span class="no">GithubActionsAPI</span><span class="p">.</span><span class="nf">update_secret</span><span class="p">(</span><span class="n">github_api_token</span><span class="p">,</span> <span class="no">Config</span><span class="o">::</span><span class="no">GITHUB</span><span class="p">[</span><span class="ss">:scope</span><span class="p">],</span> <span class="s2">"FASTLANE_SESSION"</span><span class="p">,</span> <span class="n">encrypted_data</span><span class="p">,</span> <span class="n">public_key_data</span><span class="p">[</span><span class="s1">'key_id'</span><span class="p">])</span></code></pre></figure>

<p>With this setup, we were able to connect manually to our main Apple account using the first script that checks periodically for the pop-up and sends the 2FA code to slack. And we had our <code class="language-plaintext highlighter-rouge">FASTLANE_SESSION</code> updated many times a day so that our CI uses a valid session token.</p>

<h2 id="conclusion">Conclusion</h2>
<p>Implementing a solution to properly retrieve and update our CI with a fastlane session environment variable wasn’t really a trivial task as it seemed at first. We ended up using custom scripts on one of our CI machines that is trusted by our main Apple account to retireve the code to be sent to slack or used in <em>spaceauth</em>. This is very specific to our organization, you may have other constraints, but the same ideas can be reused.</p>]]></content><author><name>Yassine Ghazouan</name></author><category term="iOS" /><category term="CI" /><summary type="html"><![CDATA[Starting in February 2021, Apple added an additional layer of security by making it mandatory to use the two-factor authentication or two-step authentication system when signing users into the App Store Connect.]]></summary></entry><entry><title type="html">Enhance Xcode templates using Git</title><link href="https://fabernovel.github.io/2021-06-22/enhance-xcode-templates-using-git" rel="alternate" type="text/html" title="Enhance Xcode templates using Git" /><published>2021-06-22T00:00:00+00:00</published><updated>2021-06-22T00:00:00+00:00</updated><id>https://fabernovel.github.io/2021-06-22/enhance-xcode-templates-using-git</id><content type="html" xml:base="https://fabernovel.github.io/2021-06-22/enhance-xcode-templates-using-git"><![CDATA[<p>Xcode provides a selection of default file and project templates. They act as a basis anytime you create a new file or a new project in Xcode.</p>

<figure>
    <img src="/assets/enhance-xcode-templates-using-git/templates-window.png" height="300px" alt="Xcode template selection window" />
    <figcaption>Xcode template selection window</figcaption>
</figure>

<p>Since old times, we can create our own templates and integrate them easily in Xcode. <code class="language-plaintext highlighter-rouge">Paul Hudson</code> wrote a <a href="https://www.hackingwithswift.com/articles/158/how-to-create-a-custom-xcode-template-for-coordinators">great post</a> about them.</p>

<p>Let see how we can use Git to power them up and create a distributed sample code codebase available right inside Xcode!</p>

<h2 id="creating-custom-xcode-templates">Creating custom Xcode templates</h2>

<p>An Xcode template is basically a folder suffixed with the <code class="language-plaintext highlighter-rouge">.xctemplate</code> path extension containing some source files and some metadata. Here is an example of a <a href="https://github.com/faberNovel/CodeSnippet_iOS/tree/master/XCTemplate/Data/ADCoreDataStack.xctemplate">CoreData template</a>.</p>

<p>To populate its template selection window Xcode looks for templates in two distinctive folders on the hard drive:</p>

<ul>
  <li>The Xcode built-in folder: <code class="language-plaintext highlighter-rouge">/Applications/Xcode.app/.../Templates</code></li>
  <li>The current user folder: <code class="language-plaintext highlighter-rouge">~/Library/Developer/Xcode/Templates/</code></li>
</ul>

<p>Adding a custom template in Xcode is as simple as copying and pasting a correctly structured <code class="language-plaintext highlighter-rouge">xctemplate</code> folder into <code class="language-plaintext highlighter-rouge">~/Library/Developer/Xcode/Templates/</code>.</p>

<figure>
    <img src="/assets/enhance-xcode-templates-using-git/custom-templates-window.png" height="300px" alt="CoreData template available in Xcode" />
    <figcaption>Example of a CoreData template available in Xcode</figcaption>
</figure>

<p><code class="language-plaintext highlighter-rouge">Paul Hudson</code>’s <a href="https://www.hackingwithswift.com/articles/158/how-to-create-a-custom-xcode-template-for-coordinators">post</a> explains in details the Xcode template structure. Consider reading it if you want to master the template creation!</p>

<h2 id="installing-xcode-templates-from-git">Installing Xcode templates from git</h2>

<p>In his post, Paul does not offer advice on how to manage Xcode templates. If we store our Xcode templates inside an Xcode configuration folder, we are at an increased risk of losing them and we would not be able to share them accross multiple machines. Storing our templates inside a git repository sounds like the best way to go.</p>

<p>But manually downloading a git repository into the Xcode configuration folder anytime we need to update our templates sounds fastidious though. How can we link templates from a repository to Xcode?</p>

<p><a href="https://github.com/faberNovel/xcresource-cli">xcresource-cli</a> can do just that. It is a small tool aimed at managing Xcode templates. In particular, it can download templates from a specified repository URL and integrate them in Xcode:</p>

<figure class="highlight"><pre><code class="language-ssh" data-lang="ssh"><span class="err">&gt;</span> <span class="k">xcresource</span> template install --url https://github.com/faberNovel/CodeSnippet_iOS --namespace FABERNOVEL --pointer main</code></pre></figure>

<figure>
    <img src="/assets/enhance-xcode-templates-using-git/fabernovel-templates-window.png" height="300px" alt="xcresource can install templates from a git repository" />
    <figcaption>xcresource can install templates from a git repository</figcaption>
</figure>

<p>During the installation, we can specify a <code class="language-plaintext highlighter-rouge">namespace</code> parameter. It acts as an installation folder and allows us to install templates from different sources:</p>

<figure class="highlight"><pre><code class="language-ssh" data-lang="ssh"><span class="err">//</span> <span class="k">install</span> the templates from the main branch of the repo
<span class="err">&gt;</span> <span class="k">xcresource</span> template install --url https://github.com/faberNovel/CodeSnippet_iOS --namespace MAIN --pointer main

<span class="err">//</span> <span class="k">install</span> the templates from the develop branch of the repo
<span class="err">&gt;</span> <span class="k">xcresource</span> template install --url https://github.com/faberNovel/CodeSnippet_iOS --namespace DEVELOP --pointer develop

<span class="err">//</span> <span class="k">only</span> remove the folder containing the templates from the develop branch of the repo
<span class="err">&gt;</span> <span class="k">xcresource</span> template remove --namespace DEVELOP</code></pre></figure>

<h2 id="using-xcode-templates-as-a-distributed-sample-code-database">Using Xcode templates as a distributed sample code database</h2>

<p>Coupling Xcode templates with git can be very powerful. On one hand, Git allows us to distribute our templates and make contributing to them easy. On the other hand, thanks to the flexibility offered by Xcode, our templates can be displayed and selected right inside Xcode.</p>

<figure>
    <img src="/assets/enhance-xcode-templates-using-git/distributed-sample-code-database.png" height="280px" alt="xcresource can install templates from a git repository" />
</figure>

<p>At Fabernovel, we use such sample code database. All our sample codes are stored in the <a href="https://github.com/faberNovel/CodeSnippet_iOS">code snippets repository</a> which acts as a sample code reference repository. Anytime a sample code wouldn’t fit inside a library because it is too small, too variable or simple boilerplate code, we store it inside this repository. It mainly contains basic UI elements, boilerplate code, helper extensions, algorithms etc. Because all the sample codes are written as <a href="https://github.com/faberNovel/CodeSnippet_iOS/tree/master/XCTemplate">Xcode templates</a>, they are all available in Xcode anytime we create a new file thanks to <code class="language-plaintext highlighter-rouge">xcresource-cli</code>.</p>

<p>The code snippet repository perfectly complements the tools we already use in our Fabernovel developer day-to-day life to be more productive and avoid boilerplate:</p>

<ul>
  <li>A project templater we use anytime we start a new iOS project</li>
  <li><a href="https://github.com/faberNovel/ADUtils">ADUtils</a> a set of helpers, shortcuts or other tools providing simplified interactions with UIKit and more generally with Swift.</li>
  <li><a href="https://github.com/felginep/ccios">ccios</a> an Xcode file generator for Clean Code architecture</li>
</ul>

<h2 id="whats-next">What’s next?</h2>

<p>Try to create your own Xcode template repository and display them in Xcode of course!</p>

<p>You can read <a href="https://www.hackingwithswift.com/articles/158/how-to-create-a-custom-xcode-template-for-coordinators">Hudson’s post</a> and <a href="https://github.com/faberNovel/xcresource-cli">xcresource-cli’s readme</a> to get a better overview of what you can do with Xcode templates and Git.</p>

<p>Discover how you can use <code class="language-plaintext highlighter-rouge">xcresource</code> to <a href="https://fabernovel.github.io/2021-07-22/enhance-xcode-snippets-using-git">enhance your Xcode codesnippets</a> ;)</p>]]></content><author><name>Gaétan Zanella</name></author><category term="iOS" /><category term="macOS" /><summary type="html"><![CDATA[Xcode provides a selection of default file and project templates. They act as a basis anytime you create a new file or a new project in Xcode.]]></summary></entry><entry><title type="html">Simplify collection view compositional layout with a DSL</title><link href="https://fabernovel.github.io/2021-05-18/simplify-collection-view-compositional-layout-with-a-dsl" rel="alternate" type="text/html" title="Simplify collection view compositional layout with a DSL" /><published>2021-05-18T00:00:00+00:00</published><updated>2021-05-18T00:00:00+00:00</updated><id>https://fabernovel.github.io/2021-05-18/simplify-collection-view-compositional-layout%E2%80%94with-a-dsl</id><content type="html" xml:base="https://fabernovel.github.io/2021-05-18/simplify-collection-view-compositional-layout-with-a-dsl"><![CDATA[<p>At the WWDC19 Apple introduced a class <code class="language-plaintext highlighter-rouge">UICollectionViewCompositionalLayout</code>. This new API allows us to create a custom layout
in a declarative and composable way. It is available from iOS 13, tvOS 13 and macOS 10.15.</p>

<p>Here are some examples of compositional layout from Apple sample code <a href="https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views">Implementing Modern Collection Views</a>:</p>

<div style="display: flex; justify-content: center; flex-wrap: wrap;">
	<figure style="margin-inline: 1em;">
		<img src="/assets/simplify-collection-view-compositional-layout—with-a-dsl/orthogonal-nested-groups.png" height="450px" alt="Layout with nested groups with orthogonal scrolling" />
		<figcaption>Layout with nested groups</figcaption>
	</figure>
	<figure style="margin-inline: 1em;">
		<img src="/assets/simplify-collection-view-compositional-layout—with-a-dsl/list-layout.png" height="450px" alt="List layout" />
		<figcaption>List layout</figcaption>
	</figure>
	<figure style="margin-inline: 1em;">
		<img src="/assets/simplify-collection-view-compositional-layout—with-a-dsl/orthogonal-scrolling.gif" height="450px" alt="Orthogonal scrolling example" />
		<figcaption>Orthogonal scrolling</figcaption>
	</figure>
</div>

<p>A compositional layout is composed of sections containing groups, and the groups contain items.
There are also supplementary items and boundary supplementary items for headers, footers or any custom supplementary view like badges.
One of the best feature of compositonal layouts is the ability to have multi-dimensional browsing with the orthogonal scrolling behavior (see gif above).</p>

<p>Here is for example the code for the layout with nested groups and orthogonal scrolling:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">leadingItem</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutItem</span><span class="p">(</span>
    <span class="nv">layoutSize</span><span class="p">:</span> <span class="kt">NSCollectionLayoutSize</span><span class="p">(</span><span class="nv">widthDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">0.7</span><span class="p">),</span>
                                       <span class="nv">heightDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalHeight</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)))</span>
<span class="n">leadingItem</span><span class="o">.</span><span class="n">contentInsets</span> <span class="o">=</span> <span class="kt">NSDirectionalEdgeInsets</span><span class="p">(</span><span class="nv">top</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">leading</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">bottom</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">trailing</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>

<span class="k">let</span> <span class="nv">trailingItem</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutItem</span><span class="p">(</span>
    <span class="nv">layoutSize</span><span class="p">:</span> <span class="kt">NSCollectionLayoutSize</span><span class="p">(</span><span class="nv">widthDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">1.0</span><span class="p">),</span>
                                       <span class="nv">heightDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalHeight</span><span class="p">(</span><span class="mf">0.3</span><span class="p">)))</span>
<span class="n">trailingItem</span><span class="o">.</span><span class="n">contentInsets</span> <span class="o">=</span> <span class="kt">NSDirectionalEdgeInsets</span><span class="p">(</span><span class="nv">top</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">leading</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">bottom</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">trailing</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">trailingGroup</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutGroup</span><span class="o">.</span><span class="nf">vertical</span><span class="p">(</span>
    <span class="nv">layoutSize</span><span class="p">:</span> <span class="kt">NSCollectionLayoutSize</span><span class="p">(</span><span class="nv">widthDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">0.3</span><span class="p">),</span>
                                       <span class="nv">heightDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalHeight</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)),</span>
    <span class="nv">subitem</span><span class="p">:</span> <span class="n">trailingItem</span><span class="p">,</span> <span class="nv">count</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span>

<span class="k">let</span> <span class="nv">containerGroup</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutGroup</span><span class="o">.</span><span class="nf">horizontal</span><span class="p">(</span>
    <span class="nv">layoutSize</span><span class="p">:</span> <span class="kt">NSCollectionLayoutSize</span><span class="p">(</span><span class="nv">widthDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">0.85</span><span class="p">),</span>
                                       <span class="nv">heightDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalHeight</span><span class="p">(</span><span class="mf">0.4</span><span class="p">)),</span>
    <span class="nv">subitems</span><span class="p">:</span> <span class="p">[</span><span class="n">leadingItem</span><span class="p">,</span> <span class="n">trailingGroup</span><span class="p">])</span>
<span class="k">let</span> <span class="nv">section</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutSection</span><span class="p">(</span><span class="nv">group</span><span class="p">:</span> <span class="n">containerGroup</span><span class="p">)</span>
<span class="n">section</span><span class="o">.</span><span class="n">orthogonalScrollingBehavior</span> <span class="o">=</span> <span class="o">.</span><span class="n">continuous</span>
<span class="k">let</span> <span class="nv">layout</span> <span class="o">=</span> <span class="kt">UICollectionViewCompositionalLayout</span><span class="p">(</span><span class="nv">section</span><span class="p">:</span> <span class="n">section</span><span class="p">)</span></code></pre></figure>

<p>This code works well, but it has some issues:</p>
<ul>
  <li>it is quite verbose</li>
  <li>it is not easy to read</li>
  <li>it is hard to visualize what this layout is building</li>
</ul>

<p>Let’s see in the following section how we can tackle these problems.</p>

<h2 id="domain-specific-language-for-uicollectionviewcompositionallayout">Domain-specific language for UICollectionViewCompositionalLayout</h2>

<p>With the Swift 5.1 <code class="language-plaintext highlighter-rouge">@_functionBuilder</code> (now <code class="language-plaintext highlighter-rouge">@resultBuilder</code> in Swift 5.4) and SwiftUI being popular,
a <a href="https://en.wikipedia.org/wiki/Domain-specific_language">domain-specific language</a> (DSL) can be a great way
to improve the <code class="language-plaintext highlighter-rouge">UICollectionViewCompositionalLayout</code> API.</p>

<p>By looking at the SwiftUI DSL created by Apple, we can model a similar syntax to rewrite the previous example into something like that:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">layout</span> <span class="o">=</span> <span class="kt">LayoutBuilder</span> <span class="p">{</span>
    <span class="kt">Section</span> <span class="p">{</span>
        <span class="kt">HGroup</span> <span class="p">{</span>
            <span class="kt">Item</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">width</span><span class="p">(</span><span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">0.7</span><span class="p">))</span>
                <span class="o">.</span><span class="nf">contentInsets</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>
            <span class="kt">VGroup</span><span class="p">(</span><span class="nv">count</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">Item</span><span class="p">()</span><span class="o">.</span><span class="nf">contentInsets</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">width</span><span class="p">(</span><span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">0.3</span><span class="p">))</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">height</span><span class="p">(</span><span class="o">.</span><span class="nf">fractionalHeight</span><span class="p">(</span><span class="mf">0.4</span><span class="p">))</span>
        <span class="o">.</span><span class="nf">width</span><span class="p">(</span><span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">0.85</span><span class="p">))</span>
    <span class="p">}</span>
    <span class="o">.</span><span class="nf">orthogonalScrollingBehavior</span><span class="p">(</span><span class="o">.</span><span class="n">continuous</span><span class="p">)</span>
<span class="p">}</span></code></pre></figure>

<p>This code has several advantages compared to the UIKit API:</p>
<ul>
  <li>it is shorter</li>
  <li>it is easy to read with a SwiftUI like syntax</li>
  <li>it is way more visual: the syntax follows the structure of the layout</li>
</ul>

<hr />

<p>Here is another example, where we focus on a simple list layout:</p>

<figure>
	<img src="/assets/simplify-collection-view-compositional-layout—with-a-dsl/animated-list-layout.gif" height="450px" alt="Custom list layout with pinned headers" />
	<figcaption>Custom list layout with pinned headers</figcaption>
</figure>

<p>With UIKit, we would implement this layout like this:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">itemSize</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutSize</span><span class="p">(</span><span class="nv">widthDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">1.0</span><span class="p">),</span>
                                      <span class="nv">heightDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalHeight</span><span class="p">(</span><span class="mf">1.0</span><span class="p">))</span>
<span class="k">let</span> <span class="nv">item</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutItem</span><span class="p">(</span><span class="nv">layoutSize</span><span class="p">:</span> <span class="n">itemSize</span><span class="p">)</span>

<span class="k">let</span> <span class="nv">groupSize</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutSize</span><span class="p">(</span><span class="nv">widthDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">1.0</span><span class="p">),</span>
                                       <span class="nv">heightDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="mi">44</span><span class="p">))</span>
<span class="k">let</span> <span class="nv">group</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutGroup</span><span class="o">.</span><span class="nf">horizontal</span><span class="p">(</span><span class="nv">layoutSize</span><span class="p">:</span> <span class="n">groupSize</span><span class="p">,</span> <span class="nv">subitems</span><span class="p">:</span> <span class="p">[</span><span class="n">item</span><span class="p">])</span>

<span class="k">let</span> <span class="nv">section</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutSection</span><span class="p">(</span><span class="nv">group</span><span class="p">:</span> <span class="n">group</span><span class="p">)</span>
<span class="n">section</span><span class="o">.</span><span class="n">interGroupSpacing</span> <span class="o">=</span> <span class="mi">5</span>
<span class="n">section</span><span class="o">.</span><span class="n">contentInsets</span> <span class="o">=</span> <span class="kt">NSDirectionalEdgeInsets</span><span class="p">(</span><span class="nv">top</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">leading</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">bottom</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">trailing</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>

<span class="k">let</span> <span class="nv">sectionHeader</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutBoundarySupplementaryItem</span><span class="p">(</span>
    <span class="nv">layoutSize</span><span class="p">:</span> <span class="kt">NSCollectionLayoutSize</span><span class="p">(</span><span class="nv">widthDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">1.0</span><span class="p">),</span>
                                       <span class="nv">heightDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">estimated</span><span class="p">(</span><span class="mi">44</span><span class="p">)),</span>
    <span class="nv">elementKind</span><span class="p">:</span> <span class="kt">PinnedSectionHeaderFooterViewController</span><span class="o">.</span><span class="n">sectionHeaderElementKind</span><span class="p">,</span>
    <span class="nv">alignment</span><span class="p">:</span> <span class="o">.</span><span class="n">top</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">sectionFooter</span> <span class="o">=</span> <span class="kt">NSCollectionLayoutBoundarySupplementaryItem</span><span class="p">(</span>
    <span class="nv">layoutSize</span><span class="p">:</span> <span class="kt">NSCollectionLayoutSize</span><span class="p">(</span><span class="nv">widthDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">fractionalWidth</span><span class="p">(</span><span class="mf">1.0</span><span class="p">),</span>
                                       <span class="nv">heightDimension</span><span class="p">:</span> <span class="o">.</span><span class="nf">estimated</span><span class="p">(</span><span class="mi">44</span><span class="p">)),</span>
    <span class="nv">elementKind</span><span class="p">:</span> <span class="kt">PinnedSectionHeaderFooterViewController</span><span class="o">.</span><span class="n">sectionFooterElementKind</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="n">sectionHeader</span><span class="o">.</span><span class="n">pinToVisibleBounds</span> <span class="o">=</span> <span class="kc">true</span>
<span class="n">sectionHeader</span><span class="o">.</span><span class="n">zIndex</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">section</span><span class="o">.</span><span class="n">boundarySupplementaryItems</span> <span class="o">=</span> <span class="p">[</span><span class="n">sectionHeader</span><span class="p">,</span> <span class="n">sectionFooter</span><span class="p">]</span>
<span class="k">let</span> <span class="nv">layout</span> <span class="o">=</span> <span class="kt">UICollectionViewCompositionalLayout</span><span class="p">(</span><span class="nv">section</span><span class="p">:</span> <span class="n">section</span><span class="p">)</span></code></pre></figure>

<p>The same layout with the DSL becomes:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">layout</span> <span class="o">=</span> <span class="kt">LayoutBuilder</span> <span class="p">{</span>
    <span class="kt">Section</span> <span class="p">{</span>
        <span class="kt">HGroup</span> <span class="p">{</span>
            <span class="kt">Item</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">height</span><span class="p">(</span><span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="mi">44</span><span class="p">))</span>
    <span class="p">}</span>
    <span class="o">.</span><span class="n">boundarySupplementaryItems</span> <span class="p">{</span>
        <span class="kt">BoundarySupplementaryItem</span><span class="p">(</span>
            <span class="nv">elementKind</span><span class="p">:</span> <span class="kt">PinnedSectionHeaderFooterViewController</span><span class="o">.</span><span class="n">sectionHeaderElementKind</span>
        <span class="p">)</span>
        <span class="o">.</span><span class="nf">height</span><span class="p">(</span><span class="o">.</span><span class="nf">estimated</span><span class="p">(</span><span class="mi">44</span><span class="p">))</span>
        <span class="o">.</span><span class="nf">alignment</span><span class="p">(</span><span class="o">.</span><span class="n">top</span><span class="p">)</span>
        <span class="o">.</span><span class="nf">pinToVisibleBounds</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
        <span class="o">.</span><span class="nf">zIndex</span><span class="p">(</span><span class="nv">zIndex</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span>
        <span class="kt">BoundarySupplementaryItem</span><span class="p">(</span>
            <span class="nv">elementKind</span><span class="p">:</span> <span class="kt">PinnedSectionHeaderFooterViewController</span><span class="o">.</span><span class="n">sectionFooterElementKind</span>
        <span class="p">)</span>
        <span class="o">.</span><span class="nf">height</span><span class="p">(</span><span class="o">.</span><span class="nf">estimated</span><span class="p">(</span><span class="mi">44</span><span class="p">))</span>
        <span class="o">.</span><span class="nf">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></figure>

<h2 id="the-compositionallayoutdsl-library">The CompositionalLayoutDSL library</h2>

<p>If you liked the proposed DSL syntax, here’s great news:<br />
The previous examples of code using the DSL can actually be used right now with our new
library <a href="https://github.com/faberNovel/CompositionalLayoutDSL">CompositionalLayoutDSL</a>!</p>

<p>This library gives you the DSL, and it also gives you the ability to compose your layout in a SwiftUI manner,
making it all the easier to reuse custom layouts, as well as improving readability.</p>

<p>Let’s take the following layout as an example:</p>
<figure>
	<img src="/assets/simplify-collection-view-compositional-layout—with-a-dsl/custom-compositional-layout.png" height="450px" alt="Custom compositional layout" />
	<figcaption>Custom compositional layout</figcaption>
</figure>

<p>You can create your own structs to represent custom groups, making them reusable in your project:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">TiledGroup</span><span class="p">:</span> <span class="kt">LayoutGroup</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">width</span><span class="p">:</span> <span class="kt">CGFloat</span>
    <span class="k">let</span> <span class="nv">inset</span><span class="p">:</span> <span class="kt">CGFloat</span>

    <span class="k">var</span> <span class="nv">layoutGroup</span><span class="p">:</span> <span class="kt">LayoutGroup</span> <span class="p">{</span>
        <span class="kt">VGroup</span> <span class="p">{</span>
            <span class="n">bigCellTrailing</span>
            <span class="n">threeSmallCells</span>
            <span class="n">bigCellLeading</span>
            <span class="n">threeSmallCells</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">height</span><span class="p">(</span><span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="n">totalHeight</span><span class="p">))</span>
        <span class="o">.</span><span class="nf">interItemSpacing</span><span class="p">(</span><span class="o">.</span><span class="nf">fixed</span><span class="p">(</span><span class="n">inset</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="c1">// Computed values</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">smallCellSize</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="p">{</span>
        <span class="nf">return</span> <span class="p">(</span><span class="n">width</span> <span class="o">-</span> <span class="n">inset</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span> <span class="o">/</span> <span class="mi">3</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">bigCellSize</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">smallCellSize</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="n">inset</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">totalHeight</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="p">{</span>
        <span class="nf">return</span> <span class="p">(</span><span class="n">bigCellSize</span> <span class="o">+</span> <span class="n">inset</span> <span class="o">+</span> <span class="n">smallCellSize</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="n">inset</span>
    <span class="p">}</span>

    <span class="c1">// Groups</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">bigCellTrailing</span><span class="p">:</span> <span class="kt">LayoutGroup</span> <span class="p">{</span>
        <span class="kt">HGroup</span> <span class="p">{</span>
            <span class="kt">VGroup</span><span class="p">(</span><span class="nv">count</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span> <span class="kt">Item</span><span class="p">()</span> <span class="p">}</span>
                <span class="o">.</span><span class="nf">width</span><span class="p">(</span><span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="n">smallCellSize</span><span class="p">))</span>
                <span class="o">.</span><span class="nf">interItemSpacing</span><span class="p">(</span><span class="o">.</span><span class="nf">fixed</span><span class="p">(</span><span class="n">inset</span><span class="p">))</span>
            <span class="kt">Item</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">width</span><span class="p">(</span><span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="n">bigCellSize</span><span class="p">))</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">height</span><span class="p">(</span><span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="n">bigCellSize</span><span class="p">))</span>
        <span class="o">.</span><span class="nf">interItemSpacing</span><span class="p">(</span><span class="o">.</span><span class="nf">fixed</span><span class="p">(</span><span class="n">inset</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">bigCellLeading</span><span class="p">:</span> <span class="kt">LayoutGroup</span> <span class="p">{</span>
        <span class="kt">HGroup</span> <span class="p">{</span>
            <span class="kt">Item</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">width</span><span class="p">(</span><span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="n">bigCellSize</span><span class="p">))</span>
            <span class="kt">VGroup</span><span class="p">(</span><span class="nv">count</span><span class="p">:</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span> <span class="kt">Item</span><span class="p">()</span> <span class="p">}</span>
                <span class="o">.</span><span class="nf">width</span><span class="p">(</span><span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="n">smallCellSize</span><span class="p">))</span>
                <span class="o">.</span><span class="nf">interItemSpacing</span><span class="p">(</span><span class="o">.</span><span class="nf">fixed</span><span class="p">(</span><span class="n">inset</span><span class="p">))</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">height</span><span class="p">(</span><span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="n">bigCellSize</span><span class="p">))</span>
        <span class="o">.</span><span class="nf">interItemSpacing</span><span class="p">(</span><span class="o">.</span><span class="nf">fixed</span><span class="p">(</span><span class="n">inset</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">threeSmallCells</span><span class="p">:</span> <span class="kt">LayoutGroup</span> <span class="p">{</span>
        <span class="kt">HGroup</span><span class="p">(</span><span class="nv">count</span><span class="p">:</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span> <span class="kt">Item</span><span class="p">()</span> <span class="p">}</span>
            <span class="o">.</span><span class="nf">height</span><span class="p">(</span><span class="o">.</span><span class="nf">absolute</span><span class="p">(</span><span class="n">smallCellSize</span><span class="p">))</span>
            <span class="o">.</span><span class="nf">interItemSpacing</span><span class="p">(</span><span class="o">.</span><span class="nf">fixed</span><span class="p">(</span><span class="n">inset</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Creating your own structs to represent your objects is possible for all components of a compositional layout.
Here is an example that conforms to <code class="language-plaintext highlighter-rouge">LayoutSection</code>:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">TiledSection</span><span class="p">:</span> <span class="kt">LayoutSection</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">inset</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">8</span>

    <span class="k">let</span> <span class="nv">environment</span><span class="p">:</span> <span class="kt">NSCollectionLayoutEnvironment</span>

    <span class="k">var</span> <span class="nv">layoutSection</span><span class="p">:</span> <span class="kt">LayoutSection</span> <span class="p">{</span>
        <span class="kt">Section</span> <span class="p">{</span>
            <span class="kt">TiledGroup</span><span class="p">(</span>
            	<span class="nv">width</span><span class="p">:</span> <span class="n">environment</span><span class="o">.</span><span class="n">container</span><span class="o">.</span><span class="n">effectiveContentSize</span><span class="o">.</span><span class="n">width</span><span class="p">,</span>
            	<span class="nv">inset</span><span class="p">:</span> <span class="n">inset</span>
            <span class="p">)</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">interGroupSpacing</span><span class="p">(</span><span class="n">inset</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>And when you want to use the custom section you configure your collection view like that:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">// To apply the layout to the collection view</span>
<span class="k">let</span> <span class="nv">compositionalLayout</span> <span class="o">=</span> <span class="kt">CompositionalLayout</span> <span class="p">{</span> <span class="n">section</span><span class="p">,</span> <span class="n">environment</span> <span class="k">in</span>
    <span class="kt">TiledSection</span><span class="p">(</span><span class="nv">environment</span><span class="p">:</span> <span class="n">environment</span><span class="p">)</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">interSectionSpacing</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="n">collectionView</span><span class="o">.</span><span class="nf">setCollectionViewLayout</span><span class="p">(</span><span class="n">compositionalLayout</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span></code></pre></figure>

<hr />

<p>All the other compositional layouts availables in the
<a href="https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views">Apple sample code</a>
have also been adapted to use the DSL, you can find the changes in
<a href="https://github.com/alexandre-pod/Apple-sample-code-Modern-Collection-Views">this repository</a>.<br />
If you want to see more of this library in action you can have a look at the <a href="https://github.com/faberNovel/CompositionalLayoutDSL/tree/main/Example">example app</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Compositional layout has been introduced by Apple in 2019 and gives you the power of creating great layouts easily.
While declarative, the UIKit API is quite verbose and therefore hard to read and visualize. <br />
To solve those issues we have created a new library <a href="https://github.com/faberNovel/CompositionalLayoutDSL">CompositionalLayoutDSL</a>
which provides a DSL that makes compositional layout code shorter, more readable and reusable.</p>

<p><a href="https://github.com/faberNovel/CompositionalLayoutDSL">CompositionalLayoutDSL</a> requires at least Swift 5.1, and is available
on the platforms supporting the compositional layout API (iOS 13+, tvOS 13+ and macOS 10.15+). You can use this library in
your project using the Swift Package Manager, CocoaPods or Carthage.</p>

<h2 id="links">Links</h2>

<ul>
  <li><a href="https://github.com/faberNovel/CompositionalLayoutDSL">CompositionalLayoutDSL library</a></li>
  <li><a href="https://developer.apple.com/documentation/uikit/uicollectionviewcompositionallayout">UICollectionViewCompositionalLayout documentation</a></li>
  <li><a href="https://developer.apple.com/documentation/appkit/nscollectionviewcompositionallayout">NSCollectionViewCompositionalLayout documentation</a></li>
  <li><a href="https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views">Apple Sample code - Implementing Modern Collection Views</a></li>
  <li><a href="https://github.com/alexandre-pod/Apple-sample-code-Modern-Collection-Views">Apple sample code modified to use our CompositionalLayoutDSL library</a></li>
</ul>]]></content><author><name>Alexandre Podlewski</name></author><category term="iOS" /><category term="tvOS" /><category term="macOS" /><summary type="html"><![CDATA[At the WWDC19 Apple introduced a class UICollectionViewCompositionalLayout. This new API allows us to create a custom layout in a declarative and composable way. It is available from iOS 13, tvOS 13 and macOS 10.15.]]></summary></entry><entry><title type="html">Our first mobile app using Kotlin Multiplatform</title><link href="https://fabernovel.github.io/2021-05-10/our-first-mobile-app-using-kotlin-multiplatform" rel="alternate" type="text/html" title="Our first mobile app using Kotlin Multiplatform" /><published>2021-05-10T00:00:00+00:00</published><updated>2021-05-10T00:00:00+00:00</updated><id>https://fabernovel.github.io/2021-05-10/our-first-mobile-app-using-kotlin-multiplatform</id><content type="html" xml:base="https://fabernovel.github.io/2021-05-10/our-first-mobile-app-using-kotlin-multiplatform"><![CDATA[<p>2021 has been a year of change for everybody, including for some of the iOS and Android developers at Fabernovel.
We have taken the opportunity to explore Kotlin Multiplatform on a production mobile app and to consider the benefits of using it on our future projects, here is the story of our journey.</p>

<p>I’m Guillaume Berthier, a software engineer and an iOS developer at Fabernovel for the past 4 years. I’ll try to explain how we use Kotlin Multiplatform from both Android and iOS points of views. Since my skill level on the Android platform is near zero, this article will mainly focus on the pros and cons an iOS developer may encounter. This should not be an issue for Android developers as it mostly uses tools they are proficient with.</p>

<h2 id="initialization">Initialization</h2>

<p>Here is the short definition of what KMM is from Jetbrains, the company which developed this technology.</p>
<blockquote>
  <p>Kotlin Multiplatform Mobile (KMM) is an SDK that allows you to use the same business logic code in both iOS and Android applications.</p>
</blockquote>

<p>When developing mobile native applications, a lot of Android and iOS code ends up doing the same thing: make a GET call to retrieve a list of something, map this JSON data into an object/class/struct, serialize it and save it into the local storage using some native frameworks depending on the platform. That serialized data might be retrieved in the future to be part of a bigger thing in a POST call and so forth.</p>

<p>It would be difficult to argue that doing the same thing twice (or more) on different platforms could be beneficial for anyone. One could argue: “I’m an iOS developer, I want to use native frameworks from my platform, I don’t want to use another cross-platform thing and spend hours to adapt the code to render the native things I want”. Fortunately, unlike other cross-platform technologies we know, KMM is not in the same category and is not here to replace the UI layer from your native mobile application. Its purpose is to write the business logic code once in Common Kotlin, produce a native iOS framework using Kotlin/Native and a native Android library using Kotlin/JVM which you can use in your Android and Xcode native projects like you would with any other native library.</p>

<figure>
    <img src="/assets/our-first-mobile-app-using-kotlin-multiplatform/common_kotlin.png" alt="Schema describing how Common Kotlin, Kotlin/Native and Kotlin/JVM interact between each other" />
    <figcaption>How Kotlin Multiplatform works</figcaption>
</figure>

<blockquote>
  <p>Kotlin/Native is primarily designed to allow compilation for platforms where virtual machines are not desirable or possible, for example, embedded devices or iOS.</p>
</blockquote>

<p>from <a href="https://kotlinlang.org/docs/native-overview.html"><em>JetBrains</em></a></p>

<p>Finally, you’re ending up with 2 mobile native applications for iOS and Android, both of them importing a library, an Objective-C framework for iOS and a Kotlin library for Android, containing your <code class="language-plaintext highlighter-rouge">Shared</code> part. All the UI components need to be developed on each platform using the native frameworks (UIKit, SwiftUI, Android UI framework, Jetpack Compose, …).</p>

<h2 id="architecture">Architecture</h2>

<p>To get the big picture, we split the project into 3 major parts:</p>
<ul>
  <li>Data, the data source and external services layer;</li>
  <li>Core, the business logic layer;</li>
  <li>App, the UI layer.</li>
</ul>

<p>When building an app using KMM, there are several ways to split your code and you’ll have to decide which part you want to share accross platform. For a first try, we’ve decided to mutualize the Data and Core parts into what we call the <code class="language-plaintext highlighter-rouge">Shared</code> module. Therefore, from the Android and iOS apps, you can see the <code class="language-plaintext highlighter-rouge">Shared</code> module as a black box exposing only <a href="https://proandroiddev.com/why-you-need-use-cases-interactors-142e8a6fe576">interactors/use cases</a> (business logic actions like “get the list of <code class="language-plaintext highlighter-rouge">People</code> filtered by name starting with <code class="language-plaintext highlighter-rouge">Gui</code>”) and the entities (all the business logic classes like <code class="language-plaintext highlighter-rouge">People</code> for instance).</p>

<figure>
	<img src="/assets/our-first-mobile-app-using-kotlin-multiplatform/initialization.png" alt="Schema describing different options to split your code" />
	<figcaption>We've decided to mutualize only the Core and Data layers</figcaption>
</figure>
<p><a href="https://danielebaroncelli.medium.com/how-much-to-share-in-kotlin-multiplatform-a-module-the-data-layer-the-view-model-5bb594eb3a2a"><em>figure source</em></a></p>

<p>The App layer contains all the UI related code: views and their view models (also called UI models on Android), view controllers and navigation logic. That layer is not in the <code class="language-plaintext highlighter-rouge">Shared</code> part because we want to develop UI using the native frameworks.
Therefore, each platform has to:</p>
<ul>
  <li>Get entities using interactors exposed out of <code class="language-plaintext highlighter-rouge">Shared</code>;</li>
  <li>Map entities into view models/UI models;</li>
  <li>Configure views using view models/UI models.</li>
</ul>

<p>That architecture lets us keep developing in the same way we would have done without using KMM, but Android and iOS platforms still have duplicated code. Both platforms need to map entities into view models/UI models in the exact same way.</p>

<p>For instance, you have an interactor in the <code class="language-plaintext highlighter-rouge">Shared</code> module called <code class="language-plaintext highlighter-rouge">GetRandomNumber</code>. That interactor returns an entity called <code class="language-plaintext highlighter-rouge">RandomNumber</code>. You map that entity into <code class="language-plaintext highlighter-rouge">RandomNumberViewModel</code>/<code class="language-plaintext highlighter-rouge">RandomNumberUIModel</code> to configure your <code class="language-plaintext highlighter-rouge">View</code>. Both platforms need to develop the same mapping logic in order to render the correct thing according to specifications. Hence, there is duplicated code meaning which could lead to different behaviors on each platform. Moreover, you’ll have to write unit tests on each platform to test your mapping.</p>

<p>Another approach would be to mutualize view models/UI models as well and let the native platforms do what they are the best at: deliver a native UI/UX experience without having to focus on business logic at all. This <a href="https://danielebaroncelli.medium.com/how-much-to-share-in-kotlin-multiplatform-a-module-the-data-layer-the-view-model-5bb594eb3a2a">great article</a> from Daniele Baroncelli talks about the different way to split your code. As it was our first time with KMM, we didn’t choose that option. For future projects and when we’ll have more experience in KMM, we’ll explore this architecture as it could be very powerful in combination with SwiftUI and Jetpack Compose.</p>

<h2 id="development">Development</h2>

<p>This section contains mainly the technical things we worked with and issues we encountered. It’s not exhaustive and we may not use them perfectly but it’s a good start if you want the big picture.</p>

<p>As you can expect, for an Android developer, KMM makes (almost) no difference. The only thing it changes is the third party libraries they use. Because <code class="language-plaintext highlighter-rouge">Shared</code> module needs to be compiled into a native iOS framework, the code it contains can’t be based on Kotlin/JVM. Hence, Kotlin libraries need to be Kotlin/Native compatible. I’ll tell you more about the libraries we used later.</p>

<h3 id="generic">Generic</h3>

<p>Generics are supported using Obj-C lightweight generics. I won’t explain how it’s working better than the official <a href="https://kotlinlang.org/docs/native-objc-interop.html#generics">documentation</a> but here is our use case.</p>

<p>Our interactors return a Kotlin sealed class <code class="language-plaintext highlighter-rouge">Result</code>. This sealed class is subclassed by two data classes <code class="language-plaintext highlighter-rouge">Success</code> and <code class="language-plaintext highlighter-rouge">Failure</code>.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="k">sealed</span> <span class="kd">class</span> <span class="nc">Result</span><span class="p">&lt;</span><span class="nc">T</span> <span class="p">:</span> <span class="nc">Any</span><span class="p">&gt;</span> <span class="p">{</span> <span class="p">}</span>
<span class="kd">data class</span> <span class="nc">Success</span><span class="p">&lt;</span><span class="nc">T</span> <span class="p">:</span> <span class="nc">Any</span><span class="p">&gt;(</span><span class="kd">val</span> <span class="py">value</span><span class="p">:</span> <span class="nc">T</span><span class="p">)</span> <span class="p">:</span> <span class="nc">Result</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;()</span>
<span class="kd">data class</span> <span class="nc">Failure</span><span class="p">&lt;</span><span class="nc">T</span> <span class="p">:</span> <span class="nc">Any</span><span class="p">&gt;(</span><span class="kd">val</span> <span class="py">error</span><span class="p">:</span> <span class="nc">YourException</span><span class="p">)</span> <span class="p">:</span> <span class="nc">Result</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;()</span></code></pre></figure>

<p>then in your Swift code:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="n">yourInteractor</span><span class="o">.</span><span class="nf">execute</span><span class="p">(</span><span class="cm">/*…*/</span><span class="p">)</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
    <span class="k">if</span> <span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">result</span> <span class="k">as?</span> <span class="kt">Success</span><span class="p">)?</span><span class="o">.</span><span class="n">value</span> <span class="k">as?</span> <span class="kt">Bool</span> <span class="cm">/*considering my interactor returns success value is a Bool*/</span> <span class="p">{</span>
        <span class="c1">// …</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="k">let</span> <span class="nv">failureResult</span> <span class="o">=</span> <span class="p">(</span><span class="n">result</span> <span class="k">as?</span> <span class="kt">Failure</span><span class="p">)?</span><span class="o">.</span><span class="n">error</span> <span class="p">{</span>
        <span class="c1">// …</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>This works perfectly even though all this casting mechanic is not elegant. 
What if <code class="language-plaintext highlighter-rouge">Result</code> is subclassed by another data class? The Swift compiler would not even notice it.</p>

<h3 id="sealed-class">Sealed class</h3>

<p>Kotlin developers use <a href="https://kotlinlang.org/docs/sealed-classes.html">Sealed classes</a> everywhere. It’s an abstract class that can only be subclassed in the file where it is defined. With this restriction, the compiler knows perfectly the class hierarchy. It’s quite powerful used in combination with the <code class="language-plaintext highlighter-rouge">when</code> expression since we can then omit the <code class="language-plaintext highlighter-rouge">else</code> branch. By knowing the class hierarchy, the compiler knows that the <code class="language-plaintext highlighter-rouge">when</code> is exhaustive and thus that a <code class="language-plaintext highlighter-rouge">else</code> branch is not required. An iOS developer can see this as an enum with associated type. Unfortunately, there is no bridging between Kotlin Sealed class and Obj-C.</p>

<p>As we saw in the previous part, casting is possible but it’s not perfect. We would prefer a system where we get a compilation error if we forget to handle a new sealed class subclass.</p>

<p>Hence, we implemented the following, adding a <code class="language-plaintext highlighter-rouge">fold</code> method on our sealed classes. Combined with a linter rule, you can warn the developer if he forgot to add this method when he exposes his sealed class.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="k">fun</span> <span class="p">&lt;</span><span class="nc">R</span><span class="p">&gt;</span> <span class="nf">fold</span><span class="p">(</span>
    <span class="n">onSuccess</span><span class="p">:</span> <span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nc">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">R</span><span class="p">,</span>
    <span class="n">onFailure</span><span class="p">:</span> <span class="p">(</span><span class="n">exception</span><span class="p">:</span> <span class="nc">YourException</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">R</span>
<span class="p">):</span> <span class="nc">R</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">when</span> <span class="p">(</span><span class="k">this</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">is</span> <span class="nc">Success</span> <span class="p">-&gt;</span> <span class="nf">onSuccess</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
        <span class="k">is</span> <span class="nc">Failure</span> <span class="p">-&gt;</span> <span class="nf">onFailure</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>then in your Swift code:</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="n">yourInteractor</span><span class="o">.</span><span class="nf">execute</span><span class="p">(</span><span class="cm">/*…*/</span><span class="p">)</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
    <span class="n">result</span><span class="o">.</span><span class="nf">fold</span><span class="p">(</span><span class="nv">onSuccess</span><span class="p">:</span> <span class="p">{</span> <span class="n">value</span> <span class="k">in</span>
        <span class="c1">// …</span>
    <span class="p">},</span> <span class="nv">onFailure</span><span class="p">:</span> <span class="p">{</span> <span class="n">error</span> <span class="k">in</span>
        <span class="c1">// …</span>
    <span class="p">})</span>
<span class="p">}</span></code></pre></figure>

<p>If a developer adds a new <code class="language-plaintext highlighter-rouge">Result</code> subclass, he’ll get an error from Kotlin as long as he does not handle the new class in the <code class="language-plaintext highlighter-rouge">fold</code> method.</p>

<h3 id="interoperability">Interoperability</h3>

<p>You can see all the details on <a href="https://kotlinlang.org/docs/native-objc-interop.html">this page</a> for the interoperability details.</p>

<p>In practice, if we consider the previous example where our interactor returns a <code class="language-plaintext highlighter-rouge">Bool</code> value. Due to Obj-C bridging (remember your <code class="language-plaintext highlighter-rouge">Shared</code> library is an Objective-C framework), all the Kotlin native types like boolean, integer, … are mapped to <code class="language-plaintext highlighter-rouge">NSNumber</code>. <code class="language-plaintext highlighter-rouge">Shared</code> library is delivered in the Xcode project with the framework and the header declaring everything we can use from outside that framework. If you take a look at that header, you can notice the first part is dedicated to bridging Kotlin types into Obj-C types.</p>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"KotlinNumber"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">LQSNumber</span> <span class="p">:</span> <span class="nc">NSNumber</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithChar</span><span class="p">:(</span><span class="kt">char</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithUnsignedChar</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithShort</span><span class="p">:(</span><span class="kt">short</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithUnsignedShort</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">short</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithInt</span><span class="p">:(</span><span class="kt">int</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithUnsignedInt</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithLong</span><span class="p">:(</span><span class="kt">long</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithUnsignedLong</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithLongLong</span><span class="p">:(</span><span class="kt">long</span> <span class="kt">long</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithUnsignedLongLong</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithFloat</span><span class="p">:(</span><span class="kt">float</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithDouble</span><span class="p">:(</span><span class="kt">double</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithBool</span><span class="p">:(</span><span class="n">BOOL</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithInteger</span><span class="p">:(</span><span class="n">NSInteger</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithUnsignedInteger</span><span class="p">:(</span><span class="n">NSUInteger</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithChar</span><span class="p">:(</span><span class="kt">char</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithUnsignedChar</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithShort</span><span class="p">:(</span><span class="kt">short</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithUnsignedShort</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">short</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithInt</span><span class="p">:(</span><span class="kt">int</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithUnsignedInt</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithLong</span><span class="p">:(</span><span class="kt">long</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithUnsignedLong</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithLongLong</span><span class="p">:(</span><span class="kt">long</span> <span class="kt">long</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithUnsignedLongLong</span><span class="p">:(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithFloat</span><span class="p">:(</span><span class="kt">float</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithDouble</span><span class="p">:(</span><span class="kt">double</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithBool</span><span class="p">:(</span><span class="n">BOOL</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithInteger</span><span class="p">:(</span><span class="n">NSInteger</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithUnsignedInteger</span><span class="p">:(</span><span class="n">NSUInteger</span><span class="p">)</span><span class="nv">value</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">unavailable</span><span class="p">));</span>
<span class="k">@end</span><span class="p">;</span>

<span class="c1">// …</span>

<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"KotlinInt"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">LQSInt</span> <span class="p">:</span> <span class="nc">LQSNumber</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithInt</span><span class="p">:(</span><span class="kt">int</span><span class="p">)</span><span class="nv">value</span><span class="p">;</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithInt</span><span class="p">:(</span><span class="kt">int</span><span class="p">)</span><span class="nv">value</span><span class="p">;</span>
<span class="k">@end</span><span class="p">;</span>

<span class="c1">//…</span>

<span class="n">__attribute__</span><span class="p">((</span><span class="n">swift_name</span><span class="p">(</span><span class="s">"KotlinBoolean"</span><span class="p">)))</span>
<span class="k">@interface</span> <span class="nc">LQSBoolean</span> <span class="p">:</span> <span class="nc">LQSNumber</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithBool</span><span class="p">:(</span><span class="n">BOOL</span><span class="p">)</span><span class="nv">value</span><span class="p">;</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">numberWithBool</span><span class="p">:(</span><span class="n">BOOL</span><span class="p">)</span><span class="nv">value</span><span class="p">;</span>
<span class="k">@end</span><span class="p">;</span></code></pre></figure>

<p>In your Swift code, you do not get <code class="language-plaintext highlighter-rouge">Bool</code> out of the <code class="language-plaintext highlighter-rouge">Shared</code> module but <code class="language-plaintext highlighter-rouge">KotlinBoolean</code>. That could be weird at first sight but remember types like <code class="language-plaintext highlighter-rouge">KotlinBoolean</code> are <code class="language-plaintext highlighter-rouge">NSNumber</code> which in turn are <code class="language-plaintext highlighter-rouge">NSValue</code>. So you can leverage bridging methods and properties like <code class="language-plaintext highlighter-rouge">boolValue</code> or <code class="language-plaintext highlighter-rouge">intValue</code> to end up with Swift native types.</p>

<h3 id="concurrency">Concurrency</h3>

<p>This is a massive subject and again, we’ll focus on what we spent a lot of time.
In the <code class="language-plaintext highlighter-rouge">Shared</code> module, we use <a href="https://kotlinlang.org/docs/coroutines-guide.html">coroutines</a> and all the things an Android developer uses on a “classic” project.</p>

<p>When dealing with concurrency, you’ll mainly encounter two kind of errors.</p>

<h4 id="invalidmutabilityexception">InvalidMutabilityException</h4>

<p>As it’s written <a href="https://kotlinlang.org/docs/mobile/concurrency-overview.html#rules-for-state-sharing">here</a>, Kotlin/Native introduces 2 rules for sharing states between threads.</p>
<ul>
  <li>Mutable state == 1 thread﻿</li>
  <li>Immutable state == many threads</li>
</ul>

<p>Kotlin/Native also introduce a state called <em>frozen</em> meaning that:</p>
<ul>
  <li>a frozen object (and all the object it references) is immutable.</li>
</ul>

<p>Given those 3 predicates, if you try to update a frozen object from multiple threads, you’ll end up with an <code class="language-plaintext highlighter-rouge">InvalidMutabilityException</code>. 
Even though you do not explicitly call the <code class="language-plaintext highlighter-rouge">freeze()</code> method on your objects, you can still get that exception since third party libraries, like the one we use to handle networking, may call it to manipulate an object through multiple threads. Consider one of your object A with a reference to a singleton B. If you pass your object A to a library method that freezes it (or you freeze it yourself), both your objects A and B are now frozen. Then, if you call one of your singleton B method on another thread, you’ll end up with that exception. Therefore, make sure to call <code class="language-plaintext highlighter-rouge">freeze()</code> when it makes sense and make sure to never freeze classes that may be called from different threads by calling <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.concurrent/ensure-never-frozen.html"><code class="language-plaintext highlighter-rouge">ensureNeverFrozen()</code></a>.</p>

<h4 id="incorrectdereferenceexception">IncorrectDereferenceException</h4>

<p>This error occurs when an object, that is not frozen, is instanciated on a thread and is used from another thread. In our case, we use kodein to handle DI in our <code class="language-plaintext highlighter-rouge">Shared</code> module. All the interactors are bound in a global property <code class="language-plaintext highlighter-rouge">interactorModule</code> and exposed to public using another class.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="k">internal</span> <span class="kd">val</span> <span class="py">interactorModule</span> <span class="p">=</span> <span class="nc">DI</span><span class="p">.</span><span class="nc">Module</span><span class="p">(</span><span class="n">name</span> <span class="p">=</span> <span class="s">"interactorModule"</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">bind</span><span class="p">&lt;</span><span class="nc">MyInteractor</span><span class="p">&gt;()</span> <span class="n">with</span> <span class="nf">provider</span> <span class="p">{</span>
        <span class="nc">MyInteractor</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="c1">//…</span>
<span class="p">}</span></code></pre></figure>

<p>Be careful to access global properties from the main thread in order to avoid <a href="https://kotlinlang.org/docs/mobile/concurrency-overview.html#global-properties"><code class="language-plaintext highlighter-rouge">IncorrectDereferenceException</code></a>.</p>

<p>Apart from this 2 situations, we used concurrency as usual without encountering any other issues.</p>

<h3 id="expectactual-mechanism">Expect/Actual mechanism</h3>

<p>As I said in the architecture part, we developed a single <code class="language-plaintext highlighter-rouge">Shared</code> library and import it in our iOS and Android apps to mutualize business logic in a single codebase. To give more details, the <code class="language-plaintext highlighter-rouge">Shared</code> module is also split in 3 parts :</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">commonMain</code> : where the common code is, written in Common Kotlin;</li>
  <li><code class="language-plaintext highlighter-rouge">iosMain</code> : where the iOS part of the common code is, written in Kotlin/Native;</li>
  <li><code class="language-plaintext highlighter-rouge">androidMain</code> : where the Android part of the common code is, written in Kotlin/JVM.</li>
</ul>

<p>You can specify different behaviors for a specific platform using the <a href="https://kotlinlang.org/docs/mpp-connect-to-apis.html">actual/expect mechanism</a>.
If you declare an <code class="language-plaintext highlighter-rouge">expect</code> interface or function in common, you’ll have to provide <code class="language-plaintext highlighter-rouge">actual</code> implementations in iosMain and androidMain using the available classes from each platform. Kotlin/Native provides <a href="https://kotlinlang.org/docs/native-c-interop.html">C interoperability</a> to use native <code class="language-plaintext highlighter-rouge">NSFoundation</code> classes from Kotlin for instance.</p>

<h3 id="debug">Debug</h3>

<p>Yes, it’s possible to debug a KMM project from Xcode. I won’t get into details for the Android developers since they can debug as usual from their IDE.</p>

<p>First, you can use <a href="https://github.com/touchlab/xcode-kotlin">this plugin</a> to make Xcode recognize the Kotlin syntax. Then, you create a folder, at the root of your project for example, and add file references to Kotlin files from <code class="language-plaintext highlighter-rouge">commonMain</code> and <code class="language-plaintext highlighter-rouge">iosMain</code> directories of your <code class="language-plaintext highlighter-rouge">Shared</code> module. That’s it. You can now add breakpoints in the <code class="language-plaintext highlighter-rouge">.kt</code> files and use LLDB to debug your kotlin code.</p>

<figure>
	<img src="/assets/our-first-mobile-app-using-kotlin-multiplatform/debug.png" alt="Xcode screenshot of how debugging kotlin file looks like" />
	<figcaption>Xcode screenshot of how debugging kotlin file looks like</figcaption>
</figure>

<p>Sometimes, adding a breakpoint in closures didn’t work or paused the program execution at the wrong line.
I didn’t manage to use <code class="language-plaintext highlighter-rouge">p</code> or <code class="language-plaintext highlighter-rouge">po</code> on some objects for an unknown reason.
Moreover, you’ll have to add kotlin file references to your Xcode project in order to debug. But, make sure not to add those references into your version control solution in order to keep your project clean. Besides those downsides, debugging your KMM project from Xcode is possible.</p>

<p>Another approach is to add a run configuration to your Xcode project into Android Studio. Then, run your iOS app from Android Studio. You’ll be able to add breakpoints into the kotlin files and debug the common code perfectly but you won’t be able to debug your iOS code.</p>

<h3 id="tests">Tests</h3>

<p>As all your business logic code is shared across all your platforms, you can write your unit tests a single time in the <code class="language-plaintext highlighter-rouge">Shared</code> module using <a href="https://kotlinlang.org/docs/mpp-run-tests.html">kotlin.test API</a>. Along with <code class="language-plaintext highlighter-rouge">commonMain</code>, <code class="language-plaintext highlighter-rouge">androidMain</code> and <code class="language-plaintext highlighter-rouge">iosMain</code> directories, you’ll find <code class="language-plaintext highlighter-rouge">commonTest</code>, <code class="language-plaintext highlighter-rouge">androidTest</code> and <code class="language-plaintext highlighter-rouge">iosTest</code> to write your common tests and your platform specific tests.</p>

<p>Of course, you can still write UI tests in your Xcode and Android projects to test platform specific UI related code.</p>

<h2 id="import-your-lib">Import your lib…</h2>

<p>Great, we have a <code class="language-plaintext highlighter-rouge">Shared</code> library containing all of our business logic. Wouldn’t it be great to use it in our applications?</p>

<p>Let’s see how <code class="language-plaintext highlighter-rouge">Shared</code> is imported in your Android and iOS project.</p>

<p>When you create a KMM project in Android Studio, the template generates these folders:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">androidApp</code> containing a gradle project and the Android app source files;</li>
  <li><code class="language-plaintext highlighter-rouge">iosApp </code> containing an Xcode project and the iOS app source files;</li>
  <li><code class="language-plaintext highlighter-rouge">shared</code> containing another gradle project and all the code for the <code class="language-plaintext highlighter-rouge">Shared</code> library (<code class="language-plaintext highlighter-rouge">androidMain</code>, <code class="language-plaintext highlighter-rouge">androidTest</code>, <code class="language-plaintext highlighter-rouge">iosMain</code>, <code class="language-plaintext highlighter-rouge">iosTest</code>, <code class="language-plaintext highlighter-rouge">commonMain</code>, <code class="language-plaintext highlighter-rouge">commonTest</code> we’ve seen before).</li>
</ul>

<h3 id="from-ios">…from iOS</h3>

<p>There are many ways to import your shared library in your Xcode project, you can check this <a href="https://medium.com/wantedly-engineering/different-approaches-in-consuming-kmm-modules-in-ios-7957c722b114">article</a> to see some of them.</p>

<p>The following talks about the solutions we explored and which one we chose.</p>

<h4 id="the-built-in-import">The built in import</h4>

<p>In <code class="language-plaintext highlighter-rouge">shared</code>, you can find a <code class="language-plaintext highlighter-rouge">build.gradle.kts</code> containing all the tasks of your library. First, the <code class="language-plaintext highlighter-rouge">packForXcode</code> task generates a framework under <code class="language-plaintext highlighter-rouge">xcode-frameworks</code>.</p>

<figure class="highlight"><pre><code class="language-kotlin" data-lang="kotlin"><span class="kd">val</span> <span class="py">packForXcode</span> <span class="k">by</span> <span class="n">tasks</span><span class="p">.</span><span class="nf">creating</span><span class="p">(</span><span class="nc">Sync</span><span class="o">::</span><span class="k">class</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">group</span> <span class="p">=</span> <span class="s">"build"</span>
    <span class="kd">val</span> <span class="py">mode</span> <span class="p">=</span> <span class="nc">System</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="s">"CONFIGURATION"</span><span class="p">)</span> <span class="o">?:</span> <span class="s">"DEBUG"</span>
    <span class="kd">val</span> <span class="py">sdkName</span> <span class="p">=</span> <span class="nc">System</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="s">"SDK_NAME"</span><span class="p">)</span> <span class="o">?:</span> <span class="s">"iphonesimulator"</span>
    <span class="kd">val</span> <span class="py">targetName</span> <span class="p">=</span> <span class="s">"ios"</span> <span class="p">+</span> <span class="k">if</span> <span class="p">(</span><span class="n">sdkName</span><span class="p">.</span><span class="nf">startsWith</span><span class="p">(</span><span class="s">"iphoneos"</span><span class="p">))</span> <span class="s">"Arm64"</span> <span class="k">else</span> <span class="s">"X64"</span>
    <span class="kd">val</span> <span class="py">framework</span> <span class="p">=</span> <span class="n">kotlin</span><span class="p">.</span><span class="n">targets</span><span class="p">.</span><span class="n">getByName</span><span class="p">&lt;</span><span class="nc">KotlinNativeTarget</span><span class="p">&gt;(</span><span class="n">targetName</span><span class="p">).</span><span class="n">binaries</span><span class="p">.</span><span class="nf">getFramework</span><span class="p">(</span><span class="n">mode</span><span class="p">)</span>
    <span class="n">inputs</span><span class="p">.</span><span class="nf">property</span><span class="p">(</span><span class="s">"mode"</span><span class="p">,</span> <span class="n">mode</span><span class="p">)</span>
    <span class="nf">dependsOn</span><span class="p">(</span><span class="n">framework</span><span class="p">.</span><span class="n">linkTask</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">targetDir</span> <span class="p">=</span> <span class="nc">File</span><span class="p">(</span><span class="n">buildDir</span><span class="p">,</span> <span class="s">"xcode-frameworks"</span><span class="p">)</span>
    <span class="nf">from</span><span class="p">({</span> <span class="n">framework</span><span class="p">.</span><span class="n">outputDirectory</span> <span class="p">})</span>
    <span class="nf">into</span><span class="p">(</span><span class="n">targetDir</span><span class="p">)</span>
<span class="p">}</span></code></pre></figure>

<p>Then, the <code class="language-plaintext highlighter-rouge">packForXcode</code> task is run from a script during the build phase of your Xcode project.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">shellScript <span class="o">=</span> <span class="s2">"cd </span><span class="se">\"</span><span class="nv">$SRCROOT</span><span class="s2">/..</span><span class="se">\"\n</span><span class="s2">./gradlew :shared:packForXCode -PXCODE_CONFIGURATION=</span><span class="k">${</span><span class="nv">CONFIGURATION</span><span class="k">}</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span></code></pre></figure>

<p>Finally, the freshly generated framework is imported in your Xcode project thanks to the path under <code class="language-plaintext highlighter-rouge">Framework Search Paths</code>.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">FRAMEWORK_SEARCH_PATHS <span class="o">=</span> <span class="s2">"</span><span class="si">$(</span>SRCROOT<span class="si">)</span><span class="s2">/../shared/build/xcode-frameworks"</span><span class="p">;</span></code></pre></figure>

<p>Everything works perfectly and you can start working. Actually, we did not use that solution since we would like to have 3 git repositories (for <code class="language-plaintext highlighter-rouge">Shared</code>, the iOS app and the Android app) to control versions. Moreover, we don’t want our <code class="language-plaintext highlighter-rouge">Shared</code> project to contain any references to the mobile applications. Ideally, we could import that project in any fresh new applications we would develop in the future.</p>

<h4 id="using-cocoapods-and-git-submodule">Using cocoapods and git submodule</h4>

<p>From Android Studio, you can create an empty gradle project, remove the <code class="language-plaintext highlighter-rouge">app</code> part (corresponding to the Android app), then add a KMM module. You’ll end up with only the <code class="language-plaintext highlighter-rouge">Shared</code> directory containing <code class="language-plaintext highlighter-rouge">androidMain</code>, <code class="language-plaintext highlighter-rouge">androidTest</code>, <code class="language-plaintext highlighter-rouge">iosMain</code>, <code class="language-plaintext highlighter-rouge">iosTest</code>, <code class="language-plaintext highlighter-rouge">commonMain</code>, <code class="language-plaintext highlighter-rouge">commonTest</code> we’ve seen before.</p>

<p>During our development, we’ve decided to add the <code class="language-plaintext highlighter-rouge">Shared</code> repository as a git submodule of our mobile applications project. It seems to us more flexible to do so since the <code class="language-plaintext highlighter-rouge">Shared</code> library is in its development phase.</p>

<p>Finally, we have 3 repositories, the <code class="language-plaintext highlighter-rouge">Shared</code> one knows nothing about who is using it. Both the iOS and the Android repositories import it as a git submodule.</p>

<p>Then you’ll have a choice:</p>
<ul>
  <li>You can add the <code class="language-plaintext highlighter-rouge">packForXcode</code> task in the <code class="language-plaintext highlighter-rouge">Shared</code> project as before, and call that task from a script in your Xcode project. This does the job easily since your <code class="language-plaintext highlighter-rouge">Shared</code> project can be accessed using a relative path from your Xcode project;</li>
  <li>Import your <code class="language-plaintext highlighter-rouge">Shared</code> library as a pod.</li>
</ul>

<p>We use the second solution in order to explore that way and because we already use cocoapods to import other libraries we may need.</p>

<p>First, you need to make your <code class="language-plaintext highlighter-rouge">Shared</code> project a pod. You can follow these <a href="https://kotlinlang.org/docs/native-cocoapods.html">instructions</a>. The cocoapods plugin generates a <code class="language-plaintext highlighter-rouge">.podspec</code> containing two important pieces of information.</p>

<p>As the library is imported as a native Objective-C framework, the plugin adds a path under <code class="language-plaintext highlighter-rouge">vendored_frameworks</code> where your Xcode project will have to look in order to import the framework.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">spec.vendored_frameworks <span class="o">=</span> <span class="s2">"build/cocoapods/framework/shared.framework"</span></code></pre></figure>

<p>The second thing is the podspec contains a <code class="language-plaintext highlighter-rouge">script_phases</code> to call a gradle task creating the framework at the location specified under the <code class="language-plaintext highlighter-rouge">vendored_frameworks</code>. It’s the same logic as using a script during the build phase except the script is now directly in the podspec instead of being in the Xcode project.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">spec.script_phases <span class="o">=</span> <span class="o">[</span>
    <span class="o">{</span>
        :name <span class="o">=&gt;</span> <span class="s1">'Build shared'</span>,
        :execution_position <span class="o">=&gt;</span> :before_compile,
        :shell_path <span class="o">=&gt;</span> <span class="s1">'/bin/sh'</span>,
        :script <span class="o">=&gt;</span> <span class="o">&lt;&lt;-</span><span class="no">SCRIPT</span><span class="sh">
            set -ev
            REPO_ROOT="</span><span class="nv">$PODS_TARGET_SRCROOT</span><span class="sh">"
            "</span><span class="nv">$REPO_ROOT</span><span class="sh">/../gradlew" -p "</span><span class="nv">$REPO_ROOT</span><span class="sh">" :shared:syncFramework </span><span class="se">\</span><span class="sh">
                -Pkotlin.native.cocoapods.target=</span><span class="nv">$KOTLIN_TARGET</span><span class="sh"> </span><span class="se">\</span><span class="sh">
                -Pkotlin.native.cocoapods.configuration=</span><span class="nv">$CONFIGURATION</span><span class="sh"> </span><span class="se">\</span><span class="sh">
                -Pkotlin.native.cocoapods.cflags="</span><span class="nv">$OTHER_CFLAGS</span><span class="sh">" </span><span class="se">\</span><span class="sh">
                -Pkotlin.native.cocoapods.paths.headers="</span><span class="nv">$HEADER_SEARCH_PATHS</span><span class="sh">" </span><span class="se">\</span><span class="sh">
                -Pkotlin.native.cocoapods.paths.frameworks="</span><span class="nv">$FRAMEWORK_SEARCH_PATHS</span><span class="sh">"
</span><span class="no">        SCRIPT
</span>    <span class="o">}</span>
<span class="o">]</span></code></pre></figure>

<p>When you think your shared library is production ready, you could publish your pod on a public or private repository, then import it as any other pod without using gitsubmodule. By doing that, you should keep in mind your pod comes with the pre-compiled framework at the path you specified in <code class="language-plaintext highlighter-rouge">vendored_frameworks</code>. I did not fully explore how to do it but here is my action plan if I have to publish my pod:</p>

<ul>
  <li>Either use tasks like <code class="language-plaintext highlighter-rouge">generateDummyFramework</code> to generate a “dummy” framework or <code class="language-plaintext highlighter-rouge">linkReleaseFrameworkIosArm64</code> to generate a production ready library for iOS device.</li>
  <li>Set the correct path under <code class="language-plaintext highlighter-rouge">vendored_frameworks</code> in order for your Xcode project to be able to find the framework;</li>
  <li>Keep in mind users of your lib may not want to use the release/Arm64 version of your pod. They may want to build and link it using debug and X64 version to test it locally;</li>
  <li>Publish your pod and the sources (gradle files, <code class="language-plaintext highlighter-rouge">common/</code> and <code class="language-plaintext highlighter-rouge">iosApp/</code> directories) since another developer may need to build (using <code class="language-plaintext highlighter-rouge">gradlew</code>) the library locally with another configuration.</li>
</ul>

<h3 id="from-android">…from Android</h3>

<p>As explained in the iOS part, the <code class="language-plaintext highlighter-rouge">Shared</code> library is a dedicated project with its own <code class="language-plaintext highlighter-rouge">settings.gradle</code> and <code class="language-plaintext highlighter-rouge">build.gradle</code> at the root path and hosted in a dedicated repository. To integrate the generated Android library <code class="language-plaintext highlighter-rouge">.aar</code>, we considered two options:</p>

<ul>
  <li>Continuously deliver snapshots of the library to a maven repository and import it as a maven dependency into the Android project;</li>
  <li>Somehow integrate the sources of the <code class="language-plaintext highlighter-rouge">Shared</code> project directly into the Android project as a gradle module and build it from source.</li>
</ul>

<p>The two options are interesting but we chose the second one to iterate faster during the development phase. With this solution, we were able to modify the <code class="language-plaintext highlighter-rouge">Shared</code> project in real time and test our changes directly without having to deliver an intermediate version each time.</p>

<p>The main challenge of this option was to integrate an existing gradle project (the <code class="language-plaintext highlighter-rouge">Shared</code> project with its own <code class="language-plaintext highlighter-rouge">settings.gradle</code>) into another gradle project (our Android application), without having to duplicate the content of the root <code class="language-plaintext highlighter-rouge">settings.gradle</code> and <code class="language-plaintext highlighter-rouge">build.gradle</code>. Thanks to this <a href="https://stackoverflow.com/questions/12154031/multiple-settings-gradle-files-for-multiple-projects-building">stackoverflow thread</a>, we were able to find an elegant solution to this issue:</p>

<p>In the <code class="language-plaintext highlighter-rouge">Shared</code> project, we have two files:</p>

<ul>
  <li>A classic <code class="language-plaintext highlighter-rouge">settings.gradle</code> file that will be used to build the standalone <code class="language-plaintext highlighter-rouge">Shared</code> library (using CI/CD for instance);</li>
  <li>A <code class="language-plaintext highlighter-rouge">settings-parent.gradle</code> that includes the previous <code class="language-plaintext highlighter-rouge">settings.gradle</code> and fixes relative path to the parent project. Finally, the Android project includes this file and not the <code class="language-plaintext highlighter-rouge">settings.gradle</code></li>
</ul>

<figure class="highlight"><pre><code class="language-groovy" data-lang="groovy"><span class="c1">// settings-parent.gradle</span>
<span class="n">apply</span> <span class="nl">from:</span> <span class="s1">'settings.gradle.kts'</span>

<span class="kt">def</span> <span class="nf">updateProjectPaths</span><span class="o">(</span><span class="n">Set</span><span class="o">&lt;</span><span class="n">ProjectDescriptor</span><span class="o">&gt;</span> <span class="n">projects</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">projects</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span> <span class="n">ProjectDescriptor</span> <span class="n">project</span> <span class="o">-&gt;</span>
        <span class="n">String</span> <span class="n">relativeProjectPath</span> <span class="o">=</span> <span class="n">project</span><span class="o">.</span><span class="na">projectDir</span><span class="o">.</span><span class="na">path</span><span class="o">.</span><span class="na">replace</span><span class="o">(</span><span class="n">settingsDir</span><span class="o">.</span><span class="na">path</span><span class="o">,</span> <span class="s2">""</span><span class="o">)</span>
        <span class="n">project</span><span class="o">.</span><span class="na">projectDir</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="s2">"shared/$relativeProjectPath"</span><span class="o">)</span>
        <span class="c1">// Recursively update paths for all children</span>
        <span class="n">updateProjectPaths</span><span class="o">(</span><span class="n">project</span><span class="o">.</span><span class="na">children</span><span class="o">)</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="n">updateProjectPaths</span><span class="o">(</span><span class="n">rootProject</span><span class="o">.</span><span class="na">children</span><span class="o">)</span></code></pre></figure>

<p>In the Android project, we integrate the <code class="language-plaintext highlighter-rouge">Shared</code> submodule this way:</p>

<figure class="highlight"><pre><code class="language-groovy" data-lang="groovy"><span class="c1">// build.gradle</span>
<span class="n">apply</span> <span class="nl">from:</span> <span class="s1">'shared/settings-parent.gradle'</span>
<span class="c1">// other module declarations</span>
<span class="n">include</span> <span class="s1">':&lt;mysharedmodule&gt;'</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-groovy" data-lang="groovy"><span class="c1">// app/build.gradle</span>
<span class="n">android</span> <span class="o">{</span>
    <span class="c1">// …</span>
    <span class="n">dependencies</span> <span class="o">{</span>
        <span class="n">api</span> <span class="nf">project</span><span class="o">(</span><span class="s1">':&lt;mysharedmodule&gt;'</span><span class="o">)</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>That’s it! We can use everything we are used to: autocompletion in real time, debugger, …</p>

<p>However, there are still downsides. Incremental builds in Android Studio are not triggering the annotation processor (<a href="https://kotlinlang.org/docs/kapt.html">kapt</a>) when they should. Most of the time, kapt is not triggered and the build fails with a compilation error when adding or injecting new classes using Dagger. Make sure to clean build in order to fix that issue.</p>

<p>In the end, the benefits of this method outweigh the disadvantages!</p>

<h2 id="third-party-libraries">Third party libraries</h2>

<p>Here is a list of the lib we used in the <code class="language-plaintext highlighter-rouge">Shared</code> module for the <code class="language-plaintext highlighter-rouge">commonMain</code> part:</p>
<ul>
  <li><a href="https://github.com/Kodein-Framework/Kodein-DI">Kodein-DI</a> for dependency injection;</li>
  <li><a href="https://github.com/Kotlin/kotlinx.coroutines">Kotlinx coroutines</a> for coroutines in multiplatform;</li>
  <li><a href="https://ktor.io/docs/http-client-multiplatform.html">Ktor</a> for network;</li>
  <li><a href="https://github.com/russhwolf/multiplatform-settings">Multiplatform Settings</a> to persist key-value data (as UserDefaults and SharedPreferences would do);</li>
  <li><a href="https://github.com/touchlab/Kermit">Kermit</a> for logging;</li>
  <li><a href="https://kotlinlang.org/docs/mobile/configure-sqldelight-for-data-storage.html">SQLDelight</a> for databases.</li>
</ul>

<p>Also, you can import libraries for <code class="language-plaintext highlighter-rouge">androidMain</code> or <code class="language-plaintext highlighter-rouge">iosMain</code> only. Actually, most of the libraries you’ll import in the <code class="language-plaintext highlighter-rouge">commonMain</code> need a specific import in the <code class="language-plaintext highlighter-rouge">androidMain</code> and <code class="language-plaintext highlighter-rouge">iosMain</code> in order to work properly (see Ktor specifications).</p>

<p>Finally, if you use cocoapods plugin to make your <code class="language-plaintext highlighter-rouge">Shared</code> module a pod dependency, you can directly <a href="https://kotlinlang.org/docs/native-cocoapods.html#add-a-dependency-on-a-pod-library-from-the-cocoapods-repository">import pod</a> from kotlin code as you would do in a classic Podfile.</p>

<h2 id="team-organization">Team organization</h2>

<p>This new way of building applications means our team has to adopt another organization. With hindsight, I think the way developers will work on the application depends on, from a list of other things, the part of your application you will mutualize in the <code class="language-plaintext highlighter-rouge">Shared</code> code. As I said, in our project, we’ve decided to share only the Data and the Core layers across platforms while another approach would be to share the Data, the Core and only view models/UI models of the App layers to let the native code handles only the UI components.</p>

<p>The main benefit of our approach is to start exploring KMM while keeping the same architecture we used so far on Android and iOS platform. As Kotlin Multiplatform library is written… in Kotlin, Android team developed almost everything in the <code class="language-plaintext highlighter-rouge">Shared</code> module.</p>

<p>Here is a non exhaustive list of things, I think, developers should pay attention to:</p>

<ul>
  <li>Everytime an iOS developer starts a new feature, he should talk to an Android developer to know which API of the <code class="language-plaintext highlighter-rouge">Shared</code> library to use from the Swift code and how to map the entity he’ll end up with;</li>
  <li>Try to add documentation on the public elements of your <code class="language-plaintext highlighter-rouge">Shared</code> module in order to let the users of this library (yourself, your Android and your iOS colleagues) know how to use it;</li>
  <li>As an iOS developer, open the Android project to double check how the API is used from Android developers and improve your knowledge in Kotlin;</li>
  <li>As an iOS developer, review every pull requests Android developers do in order to improve your knowledge in Kotlin;</li>
  <li>iOS developers have to know how gradle works. You can find a lot of tutorials on internet or join <a href="https://gradle.com/training/introduction-to-gradle/">free trainings</a> organized by Gradle;</li>
  <li>Try to work on the same feature at the same time. Since iOS and Android developers have to communicate a lot, it would be difficult for an Android developer to answer an iOS developer questions about features he worked on a month ago.</li>
</ul>

<p>In terms of project management, we run two-week-long SCRUM sprints. Every feature corresponds to two Jira tickets (iOS and Android). Since it’s our first project in KMM, we find out it was closer to reality to put the same amount of story point on both platform for a given feature. If you consider it does not change anything for an Android developer to develop in KMM, you can assume he works 100% on the project as usual. Then you can assume iOS developers won’t work on the <code class="language-plaintext highlighter-rouge">Shared</code> code, and so he’ll work 100% - X% on the project.</p>

<p>That theory is great but in practice, it takes time for an iOS developer to improve himself on Kotlin technology. Unless the iOS team has already worked with KMM, it seems fair to assume both team will spend the same amount of time on each platform and that X% is absorbed by the time iOS dev takes to onboard on this new technology.</p>

<p>The goal you have to keep in mind while working with KMM is to no longer have an iOS team and an Android team but to have a mobile team. When your team reaches a decent maturity level, the X% of story point you can win could be tremendous and development of the <code class="language-plaintext highlighter-rouge">Shared</code> module could be done by any developer.</p>

<h2 id="conclusion">Conclusion</h2>

<p>To sum up, if your iOS developers have time to investigate Kotlin and Gradle technologies, I think building applications using KMM would be beneficial for everyone. One of its main strength is, while you can use native components and native code to lay out your UI, you’ll no longer have to write business logic related code on every platform you want to support.</p>

<p>When we explored KMM for the first time in 2019, it seemed to us that it was not mature enough to be used in a production application. In 2021, we had a week to explore it again and we had to admit there were no technical reasons not to use it. I’m thrilled to use it again in a future project by mutualizing even more parts of the code (see Architecture part).</p>

<p>Finally, here is the <a href="https://kotlinlang.org/docs/roadmap.html">roadmap</a> if you want to keep an eye on the future.</p>

<h2 id="resources">Resources</h2>

<p>The <a href="https://kotlinlang.org/docs/mpp-intro.html">official documentation</a> from Jetbrains of course.</p>

<p>The touchlab <a href="https://touchlab.co/blog">blog</a> and most of their articles were helpful:</p>
<ul>
  <li><a href="https://touchlab.co/debug-kotlin-with-xcode-references">Debug</a></li>
  <li><a href="https://touchlab.co/multiple-kotlin-frameworks-in-application">Multiple frameworks in an app</a></li>
  <li><a href="https://touchlab.co/category/concurrency">Concurreny</a></li>
</ul>

<p>A wonderful <a href="https://helw.net/2020/04/16/multithreading-in-kotlin-multiplatform-apps/">article</a> dealing with frozen state and concurency.</p>

<p>Another serie of <a href="https://medium.com/@kpgalligan/kotlin-native-stranger-threads-c0cf0e0fb847">articles</a> from a Touchlab developer about the concurrency.</p>

<p>Some samples:</p>
<ul>
  <li><a href="https://github.com/joreilly/PeopleInSpace">PeopleInSpace</a></li>
  <li><a href="https://github.com/JetBrains/kotlinconf-app">kotlinconf-app</a></li>
  <li><a href="https://github.com/touchlab/DroidconKotlin">DroidconKotlin</a></li>
</ul>

<p>A great <a href="https://danielebaroncelli.medium.com/how-much-to-share-in-kotlin-multiplatform-a-module-the-data-layer-the-view-model-5bb594eb3a2a">article</a> about different ways to split your code.</p>

<p>A list of <a href="https://github.com/AAkira/Kotlin-Multiplatform-Libraries">KMM libraries</a> you can use.</p>]]></content><author><name>Guillaume Berthier</name></author><category term="iOS" /><category term="Android" /><summary type="html"><![CDATA[2021 has been a year of change for everybody, including for some of the iOS and Android developers at Fabernovel. We have taken the opportunity to explore Kotlin Multiplatform on a production mobile app and to consider the benefits of using it on our future projects, here is the story of our journey.]]></summary></entry><entry><title type="html">Write a REST API with a single command</title><link href="https://fabernovel.github.io/2021-05-04/rails-generators" rel="alternate" type="text/html" title="Write a REST API with a single command" /><published>2021-05-04T00:00:00+00:00</published><updated>2021-05-04T00:00:00+00:00</updated><id>https://fabernovel.github.io/2021-05-04/rails-generators</id><content type="html" xml:base="https://fabernovel.github.io/2021-05-04/rails-generators"><![CDATA[<p>At Fabernovel, when we write a REST API, we want to <a href="https://fabernovel.github.io/2021-04-15/synchronize-your-tests-with-your-documentation">synchronize our tests</a> with json schemas from our
documentation, we want to write <a href="https://fabernovel.github.io/2021-04-20/interactors-over-fat-models">interactors</a> for each CRUD operation and we also want to write our
resources inside a <a href="https://fabernovel.github.io/2021-04-27/use-domains-names-with-rails">namespace</a>. Doing these extra steps greatly increases the readability and the maintainability
of our code base. But it also increases the boilerplate a developer needs to write.</p>

<h2 id="inheritance-at-the-rescue">Inheritance at the rescue?</h2>

<p>Inheritance can be used as a refactoring tool and is a way to avoid code duplication. For example,
Active Admin uses the gem <a href="https://github.com/activeadmin/inherited_resources">inherited_resources</a>.
It allows us to create a CRUD controller by simply inheriting a base controller.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">ProjectsController</span> <span class="o">&lt;</span> <span class="no">InheritedResources</span><span class="o">::</span><span class="no">Base</span>
<span class="k">end</span></code></pre></figure>

<p>It is very simple, it follows the Convention over Configuration principle and is definitely not boilerplate.
However, there is a problem with this solution: how do you customize the behaviour ? What methods should you override ?
For example, how do you solve these use cases:</p>
<ul>
  <li>You need to transform the request before creating your resource like converting an amount with included taxes to a tax-free amount.</li>
  <li>You need to send an email after creating a resource or when an error occurred</li>
  <li>You need to perform a payment when creating a resource</li>
  <li>You need to restrict access and only respond with a subset of the current resource like delivering only the user’s projects.</li>
</ul>

<p>There are solutions but if you need to look at the documentation from <code class="language-plaintext highlighter-rouge">inherited_resources</code> then you increased
the entry barrier.</p>

<p>It is also easy to misuse this gem. Do you know when to override <code class="language-plaintext highlighter-rouge">collection</code> or <code class="language-plaintext highlighter-rouge">end_of_association_chain</code> ?<br />
Do you know when to override <code class="language-plaintext highlighter-rouge">create_resource</code> or use <code class="language-plaintext highlighter-rouge">create!</code> ? It’s easy to find the answers in the documentation but
if the code is already in in your project, you will reuse the current method without knowing if there are
better ways to solve your problem.</p>

<p>There is another flaw in this gem: we are writing fat controllers and are reducing testability and reusability. We know that
we can solve this problem with interactors. But how can we avoid writing the boilerplate code introduced by it ?</p>

<h2 id="code-generation">Code generation</h2>

<p>Rails is shipped with a generator tool built on top of <a href="https://github.com/erikhuda/thor">Thor</a>. It allow us to use a cli
to generate source code. For example, <code class="language-plaintext highlighter-rouge">rails g model Project</code> will create</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">      invoke  active_record
      create    db/migrate/20210322133155_create_projects.rb
      create    app/models/project.rb
      invoke    test_unit
      create      <span class="nb">test</span>/models/project_test.rb</code></pre></figure>

<p>We can use this tool to create our own generator that will meet our goals when it comes to architecture. We can rely on the current database
schema to write code that we would have written by hand. Generating code solves our problem of having to write boilerplate.</p>

<p>This first step is to create our generator: <code class="language-plaintext highlighter-rouge">rails g generator clean_code</code></p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">create  lib/generators/clean_code
create  lib/generators/clean_code/clean_code_generator.rb
create  lib/generators/clean_code/USAGE
create  lib/generators/clean_code/templates
invoke  test_unit
create    <span class="nb">test</span>/lib/generators/clean_code_generator_test.rb</code></pre></figure>

<h2 id="model">Model</h2>

<p>Then, we need to create two templates to create our model and the associated test. Our architecture is based
on namespaces to reflect the <a href="https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html">Screaming Architecture</a>, so
we copy our files in a domains folder.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">copy_model</span>
  <span class="n">template</span> <span class="s1">'model.rb.erb'</span><span class="p">,</span> <span class="s2">"app/domains/</span><span class="si">#{</span><span class="n">file_path</span><span class="si">}</span><span class="s2">.rb"</span>
  <span class="n">template</span> <span class="s1">'model_test.rb.erb'</span><span class="p">,</span> <span class="s2">"test/domains/</span><span class="si">#{</span><span class="n">file_path</span><span class="si">}</span><span class="s2">_test.rb"</span>
<span class="k">end</span></code></pre></figure>

<p>The generated model will look like this:</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Project</span> <span class="o">&lt;</span> <span class="no">DomainModel</span>
  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
  <span class="n">validates</span> <span class="ss">:is_vacations</span><span class="p">,</span> <span class="ss">inclusion: </span><span class="p">[</span><span class="kp">true</span><span class="p">,</span> <span class="kp">false</span><span class="p">]</span>
  <span class="c1"># ...</span>
  
  <span class="n">belongs_to</span> <span class="ss">:company</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span><span class="p">,</span> <span class="ss">inverse_of: :projects</span>
  <span class="n">belongs_to</span> <span class="ss">:project_type</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span><span class="p">,</span> <span class="ss">inverse_of: :projects</span>
  <span class="c1"># ...</span>
  
  <span class="n">has_many</span> <span class="ss">:expenses</span><span class="p">,</span> <span class="ss">inverse_of: :project</span><span class="p">,</span> <span class="ss">dependent: :restrict_with_error</span>
  <span class="n">has_many</span> <span class="ss">:bills</span><span class="p">,</span> <span class="ss">inverse_of: :project</span><span class="p">,</span> <span class="ss">dependent: :restrict_with_error</span>
  <span class="c1"># ...</span>
<span class="k">end</span></code></pre></figure>

<p>For the model, we use database constraints to generate validations and foreign key to generate<code class="language-plaintext highlighter-rouge">belongs_to</code>/<code class="language-plaintext highlighter-rouge">has_many</code> relations.
By generating the code and not writing it, we avoid typos on relation names, we don’t forget to set <code class="language-plaintext highlighter-rouge">inverse_of</code>. 
In case we had forgotten, we think beforehand of what we should do with <code class="language-plaintext highlighter-rouge">has_many</code> relations if we try to destroy our object.
Finally, the generated test is here to ensure validations are correctly set.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">ProjectTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
  <span class="nb">test</span> <span class="s1">'should have name'</span> <span class="k">do</span>
    <span class="n">assert_not</span> <span class="n">build</span><span class="p">(</span><span class="ss">:project</span><span class="p">,</span> <span class="ss">name: </span><span class="kp">nil</span><span class="p">).</span><span class="nf">valid?</span>
  <span class="k">end</span>
  
  <span class="nb">test</span> <span class="s1">'should have is_vacations'</span> <span class="k">do</span>
    <span class="n">assert_not</span> <span class="n">build</span><span class="p">(</span><span class="ss">:project</span><span class="p">,</span> <span class="ss">is_vacations: </span><span class="kp">nil</span><span class="p">).</span><span class="nf">valid?</span>
  <span class="k">end</span>
  
  <span class="c1"># ...</span>
  
  <span class="nb">test</span> <span class="s1">'should have company'</span> <span class="k">do</span>
    <span class="n">assert_not</span> <span class="n">build</span><span class="p">(</span><span class="ss">:project</span><span class="p">,</span> <span class="ss">company: </span><span class="kp">nil</span><span class="p">).</span><span class="nf">valid?</span>
  <span class="k">end</span>
  
  <span class="nb">test</span> <span class="s1">'should have project_type'</span> <span class="k">do</span>
    <span class="n">assert_not</span> <span class="n">build</span><span class="p">(</span><span class="ss">:project</span><span class="p">,</span> <span class="ss">project_type: </span><span class="kp">nil</span><span class="p">).</span><span class="nf">valid?</span>
  <span class="k">end</span>
  
  <span class="c1"># ...</span>
<span class="k">end</span></code></pre></figure>

<p>That kind of test seems a bit dumb but there are two reasons we create them. First, we need a placeholder. If we want to add
a test, it is easier to do it when the test class already exists. And secondly, for every feature we want a test. In a perfect
world, if we <a href="https://en.wikipedia.org/wiki/Mutation_testing">mutate</a> our code, a test should fail. We don’t test the framework, we are testing that the validations are applied.
Rails validations behaviour can change. If you did the <a href="https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#active-record-belongs-to-required-by-default-option">upgrade of Rails to 5.0</a>,
you know the <code class="language-plaintext highlighter-rouge">required: true</code> is now the default option for <code class="language-plaintext highlighter-rouge">belongs_to</code>. Doing this upgrade (and probably future upgrades)
is a lot easier with a good test suite.</p>

<details>
  <summary>Templates code</summary>


<figure class="highlight"><pre><code class="language-erb" data-lang="erb"><span class="cp">&lt;%</span> <span class="n">module_namespacing</span> <span class="k">do</span> <span class="cp">-%&gt;</span>
class <span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span> <span class="nt">&lt;</span> <span class="nt">DomainModel</span><span class="cp">&lt;%</span> <span class="n">primitive_non_nullable_columns</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">column</span><span class="o">|</span> <span class="cp">%&gt;</span>
  <span class="cp">&lt;%=</span> <span class="n">column_validation</span><span class="p">(</span><span class="n">column</span><span class="p">)</span> <span class="cp">-%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span>

<span class="cp">&lt;%</span> <span class="n">belongs_to_relations</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">column</span><span class="o">|</span> <span class="cp">%&gt;</span>
  <span class="cp">&lt;%=</span> <span class="n">belongs_to</span><span class="p">(</span><span class="n">column</span><span class="p">)</span> <span class="cp">-%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span>

<span class="cp">&lt;%</span> <span class="n">has_many_relations</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">table</span><span class="o">|</span> <span class="cp">%&gt;</span>
  <span class="cp">&lt;%=</span> <span class="n">has_many</span><span class="p">(</span><span class="n">table</span><span class="p">)</span> <span class="cp">-%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
<span class="na">end</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span></code></pre></figure>



<figure class="highlight"><pre><code class="language-erb" data-lang="erb">require 'test_helper'

<span class="cp">&lt;%</span> <span class="n">module_namespacing</span> <span class="k">do</span> <span class="cp">-%&gt;</span>
class <span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span>Test <span class="nt">&lt;</span> <span class="nt">ActiveSupport::TestCase</span>
  <span class="cp">&lt;%-</span> <span class="n">non_nullable_columns</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">column</span><span class="o">|</span> <span class="cp">-%&gt;</span>
  <span class="na">test</span> <span class="err">'</span><span class="na">should</span> <span class="na">have</span> <span class="cp">&lt;%=</span> <span class="n">column</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="sr">/_id$/</span><span class="p">,</span> <span class="s1">''</span><span class="p">)</span> <span class="cp">%&gt;</span><span class="err">'</span> <span class="na">do</span>
    <span class="na">assert_not</span> <span class="na">build</span><span class="err">(</span><span class="na">:</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="err">,</span> <span class="cp">&lt;%=</span> <span class="n">column</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="sr">/_id$/</span><span class="p">,</span> <span class="s1">''</span><span class="p">)</span> <span class="cp">%&gt;</span><span class="na">:</span> <span class="na">nil</span><span class="err">).</span><span class="na">valid</span><span class="err">?</span>
  <span class="na">end</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span>
<span class="na">end</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span></code></pre></figure>

</details>

<h2 id="controller">Controller</h2>

<p>We do the same for our controller which contains the majority of the boilerplate.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">ProjectsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
  <span class="k">def</span> <span class="nf">index</span>
    <span class="n">monad</span> <span class="o">=</span> <span class="no">GetProjects</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="ss">initiator: </span><span class="n">current_user</span><span class="p">,</span> <span class="ss">params: </span><span class="n">params</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">monad</span><span class="p">.</span><span class="nf">success?</span>
      <span class="n">render_json_collection</span><span class="p">(</span><span class="n">monad</span><span class="p">.</span><span class="nf">value!</span><span class="p">,</span> <span class="ss">each_serializer: </span><span class="no">ShortProjectSerializer</span><span class="p">)</span>
    <span class="k">else</span>
      <span class="n">handle_failure</span><span class="p">(</span><span class="o">**</span><span class="n">monad</span><span class="p">.</span><span class="nf">failure</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>

  <span class="k">def</span> <span class="nf">create</span>
    <span class="n">monad</span> <span class="o">=</span> <span class="no">CreateProject</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="ss">initiator: </span><span class="n">current_user</span><span class="p">,</span> <span class="ss">params: </span><span class="n">params</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">monad</span><span class="p">.</span><span class="nf">success?</span>
      <span class="n">render_json_success</span><span class="p">(</span><span class="n">monad</span><span class="p">.</span><span class="nf">value!</span><span class="p">,</span> <span class="ss">serializer: </span><span class="no">DetailedProjectSerializer</span><span class="p">,</span> <span class="ss">status: :created</span><span class="p">)</span>
    <span class="k">else</span>
      <span class="n">handle_failure</span><span class="p">(</span><span class="o">**</span><span class="n">monad</span><span class="p">.</span><span class="nf">failure</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># ...</span>

  <span class="kp">private</span>

  <span class="k">def</span> <span class="nf">handle_failure</span><span class="p">(</span><span class="n">code</span><span class="p">:,</span> <span class="n">details</span><span class="p">:)</span>
    <span class="k">case</span> <span class="n">code</span>
    <span class="k">when</span> <span class="ss">:invalid_project</span>
      <span class="n">render_json_record_errors</span><span class="p">(</span><span class="n">details</span><span class="p">)</span>
    <span class="k">when</span> <span class="ss">:project_not_found</span>
      <span class="n">not_found</span>
    <span class="k">else</span>
      <span class="n">render_default_error_codes</span><span class="p">(</span><span class="ss">code: </span><span class="n">code</span><span class="p">,</span> <span class="ss">details: </span><span class="n">details</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>Other actions like show, update, destroy are also generated but for the sake of simplicity they are omitted. Our controllers’
mission is just to handle the http requests and responses. All the business logic is done in our interactors
(<code class="language-plaintext highlighter-rouge">GetProjects</code> and <code class="language-plaintext highlighter-rouge">CreateProject</code>, here). The contract of our interactors is that they all have a method <code class="language-plaintext highlighter-rouge">call</code> with
keywords arguments and return a monad (See <a href="https://dry-rb.org/gems/dry-monads">dry-monads</a> ). If the interactor action is
a success we simply render our project and if it is an error then we render the error using its error code. We assume the controller uses <a href="https://github.com/heartcombo/devise">devise</a>
and provides a <code class="language-plaintext highlighter-rouge">current_user</code> as more than 80% of our controllers require authentication.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">ProjectsControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
  <span class="n">setup</span> <span class="k">do</span>
    <span class="vi">@user</span> <span class="o">=</span> <span class="n">create</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span>
    <span class="n">sign_in</span> <span class="vi">@user</span>

    <span class="vi">@project</span> <span class="o">=</span> <span class="n">create</span><span class="p">(</span><span class="ss">:project</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="nb">test</span> <span class="s1">'should get projects'</span> <span class="k">do</span>
    <span class="n">get</span> <span class="n">project_projects_path</span>

    <span class="n">assert_response</span> <span class="ss">:success</span>
    <span class="n">validate_json_response_body</span> <span class="s1">'/project/get_project_projects_shortproject_200'</span>
    <span class="n">assert_not_empty</span> <span class="n">response</span><span class="p">.</span><span class="nf">parsed_body</span><span class="p">[</span><span class="s1">'projects'</span><span class="p">]</span>
  <span class="k">end</span>

  <span class="nb">test</span> <span class="s1">'should create project'</span> <span class="k">do</span>
    <span class="n">assert_difference</span> <span class="o">-&gt;</span> <span class="p">{</span> <span class="no">Project</span><span class="p">.</span><span class="nf">count</span> <span class="p">}</span> <span class="k">do</span>
      <span class="n">post_project</span>
    <span class="k">end</span>

    <span class="n">assert_response</span> <span class="ss">:created</span>
    <span class="n">validate_json_request_body</span> <span class="s1">'/project/request_post_project_projects_createproject'</span>
    <span class="n">validate_json_response_body</span> <span class="s1">'/project/post_project_projects_detailedproject_201'</span>
  <span class="k">end</span>

  <span class="nb">test</span> <span class="s1">'anonymous should not get projects'</span> <span class="k">do</span>
    <span class="n">logout</span> <span class="vi">@user</span>
    <span class="n">get</span> <span class="n">project_projects_path</span>

    <span class="n">assert_response</span> <span class="ss">:unauthorized</span>
  <span class="k">end</span>

  <span class="nb">test</span> <span class="s1">'anonymous should not create project'</span> <span class="k">do</span>
    <span class="n">logout</span> <span class="vi">@user</span>
    <span class="n">assert_no_difference</span> <span class="o">-&gt;</span> <span class="p">{</span> <span class="no">Project</span><span class="p">.</span><span class="nf">count</span> <span class="p">}</span> <span class="k">do</span>
      <span class="n">post_project</span>
    <span class="k">end</span>

    <span class="n">assert_response</span> <span class="ss">:unauthorized</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">post_project</span>
    <span class="n">post</span> <span class="n">project_projects_path</span><span class="p">,</span> <span class="ss">params: </span><span class="p">{</span>
      <span class="ss">project: </span><span class="n">attributes_for</span><span class="p">(</span><span class="ss">:project</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>The generated test looks like every rails controller test except for the <code class="language-plaintext highlighter-rouge">validate_json</code> part.
Using our internal documentation tool <a href="https://github.com/faberNovel/pericles">Pericles</a>, we can download and versionize the json schemas describing all http requests and responses for our project in our repository.
Hence, in our tests when we perform the request or receive a response, we can verify that they match the specification that we got from our documentation. Using a task, we automatically download the
schemas and save them to our VCS. Using the schemas in our tests allows us to keep the documentation up to date. 
When we run our generator, the REST endpoints that we added to our project are also automatically added to the documentation.</p>

<details>
  <summary>Templates code</summary>


<figure class="highlight"><pre><code class="language-erb" data-lang="erb"><span class="cp">&lt;%</span> <span class="n">module_namespacing</span> <span class="k">do</span> <span class="cp">-%&gt;</span>
class <span class="cp">&lt;%=</span> <span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>Controller <span class="nt">&lt;</span> <span class="nt">ApplicationController</span>
  <span class="cp">&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Get</span><span class="si">#{</span><span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>
  <span class="na">def</span> <span class="na">index</span>
    <span class="na">monad = </span><span class="s">Get</span><span class="cp">&lt;%=</span> <span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="err">.</span><span class="na">new.call</span><span class="err">(</span><span class="na">initiator:</span> <span class="na">current_user</span><span class="err">,</span> <span class="na">params:</span> <span class="na">params</span><span class="err">)</span>

    <span class="na">if</span> <span class="na">monad.success</span><span class="err">?</span>
      <span class="na">render_json_collection</span><span class="err">(</span><span class="na">monad.value</span><span class="err">!,</span> <span class="na">each_serializer:</span> <span class="na">Short</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="na">Serializer</span><span class="err">)</span>
    <span class="na">else</span>
      <span class="na">handle_failure</span><span class="err">(</span><span class="na">**monad.failure</span><span class="err">)</span>
    <span class="na">end</span>
  <span class="na">end</span>
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Get</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  <span class="na">def</span> <span class="na">show</span>
    <span class="na">monad = </span><span class="s">Get</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="err">.</span><span class="na">new.call</span><span class="err">(</span><span class="na">initiator:</span> <span class="na">current_user</span><span class="err">,</span> <span class="na">id:</span> <span class="na">params</span><span class="err">[</span><span class="na">:id</span><span class="err">])</span>

    <span class="na">if</span> <span class="na">monad.success</span><span class="err">?</span>
      <span class="na">render_json_success</span><span class="err">(</span><span class="na">monad.value</span><span class="err">!,</span> <span class="na">serializer:</span> <span class="na">Detailed</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="na">Serializer</span><span class="err">)</span>
    <span class="na">else</span>
      <span class="na">handle_failure</span><span class="err">(</span><span class="na">**monad.failure</span><span class="err">)</span>
    <span class="na">end</span>
  <span class="na">end</span>
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Update</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  <span class="na">def</span> <span class="na">update</span>
    <span class="na">monad = </span><span class="s">Update</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="err">.</span><span class="na">new.call</span><span class="err">(</span><span class="na">initiator:</span> <span class="na">current_user</span><span class="err">,</span> <span class="na">params:</span> <span class="na">params</span><span class="err">)</span>

    <span class="na">if</span> <span class="na">monad.success</span><span class="err">?</span>
      <span class="na">render_json_success</span><span class="err">(</span><span class="na">monad.value</span><span class="err">!,</span> <span class="na">serializer:</span> <span class="na">Detailed</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="na">Serializer</span><span class="err">)</span>
    <span class="na">else</span>
      <span class="na">handle_failure</span><span class="err">(</span><span class="na">**monad.failure</span><span class="err">)</span>
    <span class="na">end</span>
  <span class="na">end</span>
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Create</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  <span class="na">def</span> <span class="na">create</span>
    <span class="na">monad = </span><span class="s">Create</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="err">.</span><span class="na">new.call</span><span class="err">(</span><span class="na">initiator:</span> <span class="na">current_user</span><span class="err">,</span> <span class="na">params:</span> <span class="na">params</span><span class="err">)</span>

    <span class="na">if</span> <span class="na">monad.success</span><span class="err">?</span>
      <span class="na">render_json_success</span><span class="err">(</span><span class="na">monad.value</span><span class="err">!,</span> <span class="na">serializer:</span> <span class="na">Detailed</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="na">Serializer</span><span class="err">,</span> <span class="na">success_status:</span> <span class="na">:created</span><span class="err">)</span>
    <span class="na">else</span>
      <span class="na">handle_failure</span><span class="err">(</span><span class="na">**monad.failure</span><span class="err">)</span>
    <span class="na">end</span>
  <span class="na">end</span>
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Destroy</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  <span class="na">def</span> <span class="na">destroy</span>
    <span class="na">monad = </span><span class="s">Destroy</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="err">.</span><span class="na">new.call</span><span class="err">(</span><span class="na">initiator:</span> <span class="na">current_user</span><span class="err">,</span> <span class="na">id:</span> <span class="na">params</span><span class="err">[</span><span class="na">:id</span><span class="err">])</span>

    <span class="na">if</span> <span class="na">monad.success</span><span class="err">?</span>
      <span class="na">render_json_success</span><span class="err">(</span><span class="na">monad.value</span><span class="err">!,</span> <span class="na">serializer:</span> <span class="na">Detailed</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="na">Serializer</span><span class="err">,</span> <span class="na">success_status:</span> <span class="na">:no_content</span><span class="err">)</span>
    <span class="na">else</span>
      <span class="na">handle_failure</span><span class="err">(</span><span class="na">**monad.failure</span><span class="err">)</span>
    <span class="na">end</span>
  <span class="na">end</span>
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;</span>

  <span class="na">private</span>

  <span class="na">def</span> <span class="na">handle_failure</span><span class="err">(</span><span class="na">code:</span><span class="err">,</span> <span class="na">details:</span><span class="err">)</span>
    <span class="na">case</span> <span class="na">code</span>
    <span class="na">when</span> <span class="na">:invalid_</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>
      <span class="na">render_json_record_errors</span><span class="err">(</span><span class="na">details</span><span class="err">)</span>
    <span class="na">when</span> <span class="na">:</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="na">_not_found</span>
      <span class="na">not_found</span>
    <span class="na">else</span>
      <span class="na">render_default_error_codes</span><span class="err">(</span><span class="na">code:</span> <span class="na">code</span><span class="err">,</span> <span class="na">details:</span> <span class="na">details</span><span class="err">)</span>
    <span class="na">end</span>
  <span class="na">end</span>
<span class="na">end</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span></code></pre></figure>



<figure class="highlight"><pre><code class="language-erb" data-lang="erb">require 'test_helper'

<span class="cp">&lt;%</span> <span class="n">module_namespacing</span> <span class="k">do</span> <span class="cp">-%&gt;</span>
class <span class="cp">&lt;%=</span> <span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>ControllerTest <span class="nt">&lt;</span> <span class="nt">ActionDispatch::IntegrationTest</span>
  <span class="na">setup</span> <span class="na">do</span>
    <span class="err">@</span><span class="na">user = </span><span class="s">create(:user)</span>
    <span class="na">sign_in</span> <span class="err">@</span><span class="na">user</span>

    <span class="err">@</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span> <span class="err">=</span> <span class="na">create</span><span class="err">(</span><span class="na">:</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="err">)</span>
  <span class="na">end</span>

  <span class="cp">&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Get</span><span class="si">#{</span><span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>
  <span class="na">test</span> <span class="err">'</span><span class="na">should</span> <span class="na">get</span> <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="err">'</span> <span class="na">do</span>
    <span class="na">get</span> <span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span><span class="na">_</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="na">_path</span>

    <span class="na">assert_response</span> <span class="na">:success</span>
    <span class="na">validate_json_response_body</span> <span class="err">'/</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="err">/</span><span class="na">get_</span><span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span><span class="na">_</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="na">_short</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="na">_200</span><span class="err">'</span>
    <span class="na">assert_not_empty</span> <span class="na">response.parsed_body</span><span class="err">['</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="err">']</span>
  <span class="na">end</span>
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Get</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  <span class="na">test</span> <span class="err">'</span><span class="na">should</span> <span class="na">get</span> <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="err">'</span> <span class="na">do</span>
    <span class="na">get</span> <span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span><span class="na">_</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="na">_path</span><span class="err">(@</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="err">)</span>

    <span class="na">assert_response</span> <span class="na">:success</span>
    <span class="na">validate_json_response_body</span> <span class="err">'/</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="err">/</span><span class="na">get_</span><span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span><span class="na">_</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="na">_id_detailed</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="na">_200</span><span class="err">'</span>
  <span class="na">end</span>
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Create</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  <span class="na">test</span> <span class="err">'</span><span class="na">should</span> <span class="na">create</span> <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="err">'</span> <span class="na">do</span>
    <span class="na">assert_difference</span> <span class="na">-</span><span class="nt">&gt;</span> { <span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span>.count } do
      post_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>
    end

    assert_response :created
    validate_json_request_body '/<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>/request_post_<span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>_create<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>'
    validate_json_response_body '/<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>/post_<span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>_detailed<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>_201'
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Update</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  test 'should update <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>' do
    put_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>

    assert_response :success
    validate_json_request_body '/<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>/request_put_<span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>_id_update<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>'
    validate_json_response_body '/<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>/put_<span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>_id_detailed<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>_200'
    assert_not_equal @<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>.attributes, @<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>.reload.attributes
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Destroy</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  test 'should destroy <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>' do
    assert_difference -&gt; { <span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span>.count }, -1 do
      delete <span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>_path(@<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>)
    end

    assert_response :no_content
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Get</span><span class="si">#{</span><span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  test 'anonymous should not get <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>' do
    logout @user
    get <span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>_path

    assert_response :unauthorized
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Get</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  test 'anonymous should not get <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>' do
    logout @user
    get <span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>_path(@<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>)

    assert_response :unauthorized
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Create</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  test 'anonymous should not create <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>' do
    logout @user
    assert_no_difference -&gt; { <span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span>.count } do
      post_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>
    end

    assert_response :unauthorized
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Update</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  test 'anonymous should not update <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>' do
    logout @user
    put_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>

    assert_response :unauthorized
    assert_equal @<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>.attributes, @<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>.reload.attributes
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Destroy</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  test 'anonymous should not destroy <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>' do
    logout @user
    assert_no_difference '<span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span>.count' do
      delete <span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>_path(@<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>)
    end

    assert_response :unauthorized
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Create</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  def post_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>
    post <span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>_path, params: {
      <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>: attributes_for(:<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>)
    }
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;&lt;%-</span> <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Update</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="cp">-%&gt;</span>

  def put_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>
    put <span class="cp">&lt;%=</span> <span class="n">domain_name</span><span class="p">.</span><span class="nf">underscore</span> <span class="cp">%&gt;</span>_<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>_path(@<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>), params: {
      <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>: attributes_for(:<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>)
    }
  end
  <span class="cp">&lt;%-</span> <span class="k">end</span> <span class="cp">-%&gt;</span>
end
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span></code></pre></figure>

</details>

<details>
    <summary>Documentation creation</summary>


<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">create_documentation_on_pericles</span>
  <span class="n">config</span> <span class="o">=</span> <span class="no">YAML</span><span class="p">.</span><span class="nf">load_file</span><span class="p">(</span><span class="s1">'.pericles.yml'</span><span class="p">)</span>
  <span class="n">project_id</span> <span class="o">=</span> <span class="n">config</span><span class="p">[</span><span class="s1">'project_id'</span><span class="p">]</span>
  <span class="n">session</span> <span class="o">=</span> <span class="n">config</span><span class="p">[</span><span class="s1">'session'</span><span class="p">]</span>

  <span class="n">client</span> <span class="o">=</span> <span class="no">Pericles</span><span class="o">::</span><span class="no">Client</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">base_url: </span><span class="s1">'https://ad-pericles.herokuapp.com'</span><span class="p">,</span> <span class="ss">project_id: </span><span class="n">project_id</span><span class="p">,</span> <span class="ss">session: </span><span class="n">session</span><span class="p">)</span>
  <span class="n">json</span> <span class="o">=</span> <span class="k">begin</span>
    <span class="no">FactoryBot</span><span class="p">.</span><span class="nf">attributes_for</span><span class="p">(</span><span class="n">resource_var_name</span><span class="p">.</span><span class="nf">to_sym</span><span class="p">)</span>
  <span class="k">rescue</span> <span class="no">KeyError</span>
    <span class="no">FactoryBot</span><span class="p">.</span><span class="nf">find_definitions</span>
    <span class="no">FactoryBot</span><span class="p">.</span><span class="nf">attributes_for</span><span class="p">(</span><span class="n">resource_var_name</span><span class="p">.</span><span class="nf">to_sym</span><span class="p">)</span>
  <span class="k">end</span>
  <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_resource</span><span class="p">(</span><span class="ss">name: </span><span class="n">class_name</span><span class="p">,</span> <span class="ss">json: </span><span class="n">json</span><span class="p">)</span>
  <span class="n">response</span> <span class="o">=</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="nf">body</span><span class="p">)</span>
  <span class="n">resource_id</span> <span class="o">=</span> <span class="n">response</span><span class="p">[</span><span class="s1">'resource'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">]</span>
  <span class="n">default_resource_representation_id</span> <span class="o">=</span> <span class="n">response</span><span class="p">[</span><span class="s1">'resource'</span><span class="p">][</span><span class="s1">'resource_representations'</span><span class="p">].</span><span class="nf">first</span><span class="p">[</span><span class="s1">'id'</span><span class="p">]</span>
  <span class="n">attributes_ids</span> <span class="o">=</span> <span class="n">response</span><span class="p">[</span><span class="s1">'resource'</span><span class="p">][</span><span class="s1">'resource_attributes'</span><span class="p">].</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">data</span><span class="o">|</span> <span class="n">data</span><span class="p">[</span><span class="s1">'id'</span><span class="p">]</span> <span class="p">}</span>
  <span class="n">client</span><span class="p">.</span><span class="nf">delete_resource_representation</span><span class="p">(</span><span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">,</span> <span class="ss">representation_id: </span><span class="n">default_resource_representation_id</span><span class="p">)</span>

  <span class="n">detailed_representation</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_resource_representation</span><span class="p">(</span><span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">,</span> <span class="ss">representation_name: </span><span class="s2">"Detailed</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="ss">attributes_ids: </span><span class="n">attributes_ids</span><span class="p">)</span>
  <span class="n">short_representation</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_resource_representation</span><span class="p">(</span><span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">,</span> <span class="ss">representation_name: </span><span class="s2">"Short</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="ss">attributes_ids: </span><span class="n">attributes_ids</span><span class="p">)</span>

  <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Get</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
    <span class="n">show_route</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_route</span><span class="p">(</span><span class="ss">route_url: </span><span class="s2">"/</span><span class="si">#{</span><span class="n">collection_var_name</span><span class="si">}</span><span class="s2">/:id"</span><span class="p">,</span> <span class="ss">http_method: </span><span class="s1">'GET'</span><span class="p">,</span> <span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">)</span>
    <span class="n">client</span><span class="p">.</span><span class="nf">create_response</span><span class="p">(</span><span class="ss">route_id: </span><span class="n">show_route</span><span class="p">[</span><span class="s1">'route'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">],</span> <span class="ss">status_code: </span><span class="mi">200</span><span class="p">,</span> <span class="ss">resource_representation_id: </span><span class="n">detailed_representation</span><span class="p">[</span><span class="s1">'resource_representation'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">],</span> <span class="ss">is_collection: </span><span class="kp">false</span><span class="p">,</span> <span class="ss">root_key: </span><span class="n">resource_var_name</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Get</span><span class="si">#{</span><span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
    <span class="n">index_route</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_route</span><span class="p">(</span><span class="ss">route_url: </span><span class="s2">"/</span><span class="si">#{</span><span class="n">collection_var_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="ss">http_method: </span><span class="s1">'GET'</span><span class="p">,</span> <span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">)</span>
    <span class="n">client</span><span class="p">.</span><span class="nf">create_response</span><span class="p">(</span><span class="ss">route_id: </span><span class="n">index_route</span><span class="p">[</span><span class="s1">'route'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">],</span> <span class="ss">status_code: </span><span class="mi">200</span><span class="p">,</span> <span class="ss">resource_representation_id: </span><span class="n">short_representation</span><span class="p">[</span><span class="s1">'resource_representation'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">],</span> <span class="ss">is_collection: </span><span class="kp">true</span><span class="p">,</span> <span class="ss">root_key: </span><span class="n">collection_var_name</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Create</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
    <span class="n">create_representation</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_resource_representation</span><span class="p">(</span><span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">,</span> <span class="ss">representation_name: </span><span class="s2">"Create</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="ss">attributes_ids: </span><span class="n">attributes_ids</span><span class="p">)</span>
    <span class="n">create_route</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_route</span><span class="p">(</span><span class="ss">route_url: </span><span class="s2">"/</span><span class="si">#{</span><span class="n">collection_var_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="ss">http_method: </span><span class="s1">'POST'</span><span class="p">,</span> <span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">,</span> <span class="ss">request_root_key: </span><span class="n">resource_var_name</span><span class="p">,</span> <span class="ss">request_resource_representation_id: </span><span class="n">create_representation</span><span class="p">[</span><span class="s1">'resource_representation'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">])</span>
    <span class="n">client</span><span class="p">.</span><span class="nf">create_response</span><span class="p">(</span><span class="ss">route_id: </span><span class="n">create_route</span><span class="p">[</span><span class="s1">'route'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">],</span> <span class="ss">status_code: </span><span class="mi">201</span><span class="p">,</span> <span class="ss">resource_representation_id: </span><span class="n">detailed_representation</span><span class="p">[</span><span class="s1">'resource_representation'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">],</span> <span class="ss">is_collection: </span><span class="kp">false</span><span class="p">,</span> <span class="ss">root_key: </span><span class="n">resource_var_name</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Update</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
    <span class="n">update_representation</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_resource_representation</span><span class="p">(</span><span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">,</span> <span class="ss">representation_name: </span><span class="s2">"Update</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="ss">attributes_ids: </span><span class="n">attributes_ids</span><span class="p">)</span>
    <span class="n">update_route</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_route</span><span class="p">(</span><span class="ss">route_url: </span><span class="s2">"/</span><span class="si">#{</span><span class="n">collection_var_name</span><span class="si">}</span><span class="s2">/:id"</span><span class="p">,</span> <span class="ss">http_method: </span><span class="s1">'PUT'</span><span class="p">,</span> <span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">,</span> <span class="ss">request_root_key: </span><span class="n">resource_var_name</span><span class="p">,</span> <span class="ss">request_resource_representation_id: </span><span class="n">update_representation</span><span class="p">[</span><span class="s1">'resource_representation'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">])</span>
    <span class="n">client</span><span class="p">.</span><span class="nf">create_response</span><span class="p">(</span><span class="ss">route_id: </span><span class="n">update_route</span><span class="p">[</span><span class="s1">'route'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">],</span> <span class="ss">status_code: </span><span class="mi">200</span><span class="p">,</span> <span class="ss">resource_representation_id: </span><span class="n">detailed_representation</span><span class="p">[</span><span class="s1">'resource_representation'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">],</span> <span class="ss">is_collection: </span><span class="kp">false</span><span class="p">,</span> <span class="ss">root_key: </span><span class="n">resource_var_name</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="k">if</span> <span class="n">class_is_defined?</span><span class="p">(</span><span class="s2">"Destroy</span><span class="si">#{</span><span class="n">class_name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
    <span class="n">delete_route</span> <span class="o">=</span> <span class="n">client</span><span class="p">.</span><span class="nf">create_route</span><span class="p">(</span><span class="ss">route_url: </span><span class="s2">"/</span><span class="si">#{</span><span class="n">collection_var_name</span><span class="si">}</span><span class="s2">/:id"</span><span class="p">,</span> <span class="ss">http_method: </span><span class="s1">'DELETE'</span><span class="p">,</span> <span class="ss">resource_id: </span><span class="n">resource_id</span><span class="p">)</span>
    <span class="n">client</span><span class="p">.</span><span class="nf">create_response</span><span class="p">(</span><span class="ss">route_id: </span><span class="n">delete_route</span><span class="p">[</span><span class="s1">'route'</span><span class="p">][</span><span class="s1">'id'</span><span class="p">],</span> <span class="ss">status_code: </span><span class="mi">204</span><span class="p">,</span> <span class="ss">resource_representation_id: </span><span class="kp">nil</span><span class="p">,</span> <span class="ss">is_collection: </span><span class="kp">false</span><span class="p">,</span> <span class="ss">root_key: </span><span class="n">resource_var_name</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

</details>

<h2 id="interactors">Interactors</h2>

<p>Our interactors are straightforward and the associated tests do nothing that you would not expect.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">GetProjects</span> <span class="o">&lt;</span> <span class="no">BaseInteractor</span>
  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">initiator</span><span class="p">:,</span> <span class="ss">params: </span><span class="p">{})</span>
    <span class="n">projects</span> <span class="o">=</span> <span class="vi">@authorizer</span><span class="p">.</span><span class="nf">policy_scope</span><span class="p">(</span><span class="no">Project</span><span class="p">,</span> <span class="ss">initiator: </span><span class="n">initiator</span><span class="p">)</span>
    <span class="n">projects</span> <span class="o">=</span> <span class="n">projects</span><span class="p">.</span><span class="nf">ransack</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:q</span><span class="p">]).</span><span class="nf">result</span>
    <span class="n">projects</span> <span class="o">=</span> <span class="n">projects</span><span class="p">.</span><span class="nf">page</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:page</span><span class="p">])</span>
    <span class="no">Success</span><span class="p">(</span><span class="n">projects</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">GetProjectsTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
  <span class="n">setup</span> <span class="k">do</span>
    <span class="vi">@user</span> <span class="o">=</span> <span class="n">create</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span>
    <span class="vi">@project</span> <span class="o">=</span> <span class="n">create</span><span class="p">(</span><span class="ss">:project</span><span class="p">)</span>
  <span class="k">end</span>

  <span class="nb">test</span> <span class="s1">'should return collection of projects'</span> <span class="k">do</span>
    <span class="n">result</span> <span class="o">=</span> <span class="no">GetProjects</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="ss">initiator: </span><span class="vi">@user</span><span class="p">)</span>
    <span class="n">assert_success</span> <span class="n">result</span>
    <span class="n">projects</span> <span class="o">=</span> <span class="n">result</span><span class="p">.</span><span class="nf">value!</span>
    <span class="n">assert_equal</span> <span class="n">projects</span><span class="p">.</span><span class="nf">klass</span><span class="p">,</span> <span class="no">Project</span>
    <span class="n">assert_includes</span> <span class="n">projects</span><span class="p">,</span> <span class="vi">@project</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>We use an <code class="language-plaintext highlighter-rouge">@authorizer</code> that is a simple adapter around the <a href="https://github.com/varvet/pundit">Pundit</a> interface. We use <a href="https://github.com/activerecord-hackery/ransack">Ransack</a>
for filtering and sorting and <a href="https://github.com/kaminari/kaminari">Kaminari</a> for pagination. We don’t always use pagination but having it opt-in by default
is a good reminder to think about it. Since our interactors are plain ruby objects, it is very easy for new
developers to edit the current behaviour.</p>

<details>
    <summary>Templates code</summary>


<figure class="highlight"><pre><code class="language-erb" data-lang="erb"><span class="cp">&lt;%</span> <span class="n">module_namespacing</span> <span class="k">do</span> <span class="cp">-%&gt;</span>
class Get<span class="cp">&lt;%=</span> <span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span> <span class="nt">&lt;</span> <span class="nt">BaseInteractor</span>
  <span class="na">def</span> <span class="na">call</span><span class="err">(</span><span class="na">initiator:</span><span class="err">,</span> <span class="na">params:</span> <span class="err">{})</span>
    <span class="cp">&lt;%=</span> <span class="n">collection_var_name</span> <span class="cp">%&gt;</span> <span class="err">=</span> <span class="err">@</span><span class="na">authorizer.policy_scope</span><span class="err">(</span><span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span><span class="err">,</span> <span class="na">initiator:</span> <span class="na">initiator</span><span class="err">)</span>
    <span class="cp">&lt;%=</span> <span class="n">collection_var_name</span> <span class="cp">%&gt;</span> <span class="err">=</span> <span class="cp">&lt;%=</span> <span class="n">collection_var_name</span> <span class="cp">%&gt;</span><span class="err">.</span><span class="na">ransack</span><span class="err">(</span><span class="na">params</span><span class="err">[</span><span class="na">:q</span><span class="err">]).</span><span class="na">result</span>
    <span class="cp">&lt;%=</span> <span class="n">collection_var_name</span> <span class="cp">%&gt;</span> <span class="err">=</span> <span class="cp">&lt;%=</span> <span class="n">collection_var_name</span> <span class="cp">%&gt;</span><span class="err">.</span><span class="na">page</span><span class="err">(</span><span class="na">params</span><span class="err">[</span><span class="na">:page</span><span class="err">])</span>
    <span class="na">Success</span><span class="err">(</span><span class="cp">&lt;%=</span> <span class="n">collection_var_name</span> <span class="cp">%&gt;</span><span class="err">)</span>
  <span class="na">end</span>
<span class="na">end</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span></code></pre></figure>


<figure class="highlight"><pre><code class="language-erb" data-lang="erb">require 'test_helper'

<span class="cp">&lt;%</span> <span class="n">module_namespacing</span> <span class="k">do</span> <span class="cp">-%&gt;</span>
class Get<span class="cp">&lt;%=</span> <span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span>Test <span class="nt">&lt;</span> <span class="nt">ActiveSupport::TestCase</span>
  <span class="na">setup</span> <span class="na">do</span>
    <span class="err">@</span><span class="na">user = </span><span class="s">create(:user)</span>
    <span class="err">@</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span> <span class="err">=</span> <span class="na">create</span><span class="err">(</span><span class="na">:</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span><span class="err">)</span>
  <span class="na">end</span>

  <span class="na">test</span> <span class="err">'</span><span class="na">should</span> <span class="na">return</span> <span class="na">collection</span> <span class="na">of</span> <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="err">'</span> <span class="na">do</span>
    <span class="na">result = </span><span class="s">Get</span><span class="cp">&lt;%=</span> <span class="n">class_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="err">.</span><span class="na">new.call</span><span class="err">(</span><span class="na">initiator:</span> <span class="err">@</span><span class="na">user</span><span class="err">)</span>
    <span class="na">assert_success</span> <span class="na">result</span>
    <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span> <span class="err">=</span> <span class="na">result.value</span><span class="err">!</span>
    <span class="na">assert_equal</span> <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="err">.</span><span class="na">klass</span><span class="err">,</span> <span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span>
    <span class="na">assert_includes</span> <span class="cp">&lt;%=</span> <span class="n">resource_var_name</span><span class="p">.</span><span class="nf">pluralize</span> <span class="cp">%&gt;</span><span class="err">,</span> <span class="err">@</span><span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>
  <span class="na">end</span>
<span class="na">end</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span></code></pre></figure>

</details>

<h2 id="serializer-policies-factories">Serializer, Policies, Factories</h2>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">DetailedProjectSerializer</span> <span class="o">&lt;</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">Serializer</span>
  <span class="n">attributes</span><span class="p">(</span>
    <span class="ss">:id</span><span class="p">,</span>
    <span class="c1"># ...</span>
  <span class="p">)</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">ProjectPolicy</span> <span class="o">&lt;</span> <span class="no">ApplicationPolicy</span>
  <span class="k">class</span> <span class="nc">Scope</span> <span class="o">&lt;</span> <span class="no">Scope</span>
    <span class="k">def</span> <span class="nf">resolve</span>
      <span class="k">super</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="k">def</span> <span class="nf">permitted_attributes</span>
    <span class="p">[</span>
      <span class="ss">:name</span><span class="p">,</span>
      <span class="c1"># ...</span>
    <span class="p">]</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="no">FactoryBot</span><span class="p">.</span><span class="nf">define</span> <span class="k">do</span>
  <span class="n">factory</span> <span class="ss">:project</span><span class="p">,</span> <span class="ss">class: </span><span class="no">Project</span> <span class="k">do</span>
    <span class="n">company</span>
    <span class="n">offer_type</span>
    <span class="nb">name</span> <span class="p">{</span> <span class="s1">'name'</span> <span class="p">}</span>
    <span class="n">always_active</span> <span class="p">{</span> <span class="kp">true</span> <span class="p">}</span>
    <span class="n">start_date</span> <span class="p">{</span> <span class="no">Date</span><span class="p">.</span><span class="nf">current</span> <span class="p">}</span>
    <span class="n">number_of_days_sold</span> <span class="p">{</span> <span class="mf">42.0</span> <span class="p">}</span>
    <span class="c1"># ...</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

<p>Serializers, policies and factories are generated with all attributes. It is up to the developer to remove some if required. We find
it is a lot easier to remove attributes than to add them as you cannot add typos when removing attributes.</p>

<details>
<summary>Templates code</summary>


<figure class="highlight"><pre><code class="language-erb" data-lang="erb"><span class="cp">&lt;%</span> <span class="n">module_namespacing</span> <span class="k">do</span> <span class="cp">-%&gt;</span>
class Detailed<span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span>Serializer <span class="nt">&lt;</span> <span class="nt">ActiveModel::Serializer</span>
  <span class="na">attributes</span><span class="err">(</span><span class="cp">&lt;%</span> <span class="n">database_columns</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">column</span><span class="o">|</span> <span class="cp">%&gt;</span>
    <span class="na">:</span><span class="cp">&lt;%=</span> <span class="n">column</span><span class="p">.</span><span class="nf">name</span> <span class="cp">-%&gt;</span><span class="err">,</span><span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
  <span class="err">)</span>
<span class="na">end</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span></code></pre></figure>



<figure class="highlight"><pre><code class="language-erb" data-lang="erb"><span class="cp">&lt;%</span> <span class="n">module_namespacing</span> <span class="k">do</span> <span class="cp">-%&gt;</span>
class <span class="cp">&lt;%=</span> <span class="n">class_name</span> <span class="cp">%&gt;</span>Policy <span class="nt">&lt;</span> <span class="nt">ApplicationPolicy</span>
  <span class="na">class</span> <span class="na">Scope</span> <span class="err">&lt;</span> <span class="na">Scope</span>
    <span class="na">def</span> <span class="na">resolve</span>
      <span class="na">super</span>
    <span class="na">end</span>
  <span class="na">end</span>

  <span class="na">def</span> <span class="na">permitted_attributes</span>
    <span class="err">[</span><span class="cp">&lt;%</span> <span class="n">database_columns</span><span class="p">.</span><span class="nf">reject</span> <span class="p">{</span> <span class="o">|</span><span class="n">column</span><span class="o">|</span> <span class="sx">%w(updated_at created_at id)</span><span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="n">column</span><span class="p">.</span><span class="nf">name</span><span class="p">)</span> <span class="p">}.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">column</span><span class="o">|</span> <span class="cp">%&gt;</span>
      <span class="na">:</span><span class="cp">&lt;%=</span> <span class="n">column</span><span class="p">.</span><span class="nf">name</span> <span class="cp">-%&gt;</span><span class="err">,</span><span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
    <span class="err">]</span>
  <span class="na">end</span>
<span class="na">end</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">-%&gt;</span></code></pre></figure>



<figure class="highlight"><pre><code class="language-erb" data-lang="erb">FactoryBot.define do
  factory :<span class="cp">&lt;%=</span> <span class="n">resource_var_name</span> <span class="cp">%&gt;</span>, class: <span class="cp">&lt;%=</span> <span class="nb">name</span> <span class="cp">%&gt;</span> do<span class="cp">&lt;%</span> <span class="n">required_belongs_to_relations</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">column</span><span class="o">|</span> <span class="cp">%&gt;</span>
    <span class="cp">&lt;%=</span> <span class="n">column</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="sr">/_id$/</span><span class="p">,</span> <span class="s1">''</span><span class="p">)</span> <span class="cp">-%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;&lt;%</span> <span class="n">primitive_non_nullable_columns</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">column</span><span class="o">|</span> <span class="cp">%&gt;</span>
    <span class="cp">&lt;%=</span> <span class="n">column</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span> { <span class="cp">&lt;%=</span> <span class="n">fixture_value</span><span class="p">(</span><span class="n">column</span><span class="p">)</span> <span class="cp">%&gt;</span> }<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
  end
end</code></pre></figure>

</details>

<h2 id="conclusion">Conclusion</h2>

<p>We chose to use json schemas, interactors and domains to write quality code and maintainable Rails applications.
By using code generation to bootstrap a REST API, we don’t need to write all the boilerplate resulting from our (architectural) decisions and can
focus on the business logic of our app.</p>]]></content><author><name>Clément Villain</name></author><category term="Ruby" /><summary type="html"><![CDATA[At Fabernovel, when we write a REST API, we want to synchronize our tests with json schemas from our documentation, we want to write interactors for each CRUD operation and we also want to write our resources inside a namespace. Doing these extra steps greatly increases the readability and the maintainability of our code base. But it also increases the boilerplate a developer needs to write.]]></summary></entry></feed>