<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://davedelong.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://davedelong.com/" rel="alternate" type="text/html" /><updated>2025-01-10T16:10:56+00:00</updated><id>https://davedelong.com/feed.xml</id><title type="html">Dave DeLong</title><subtitle>He&apos;s just this guy, you know?</subtitle><entry><title type="html">Handy git customizations</title><link href="https://davedelong.com/blog/2023/03/04/handy-git-customizations/" rel="alternate" type="text/html" title="Handy git customizations" /><published>2023-03-04T00:00:00+00:00</published><updated>2023-03-04T00:00:00+00:00</updated><id>https://davedelong.com/blog/2023/03/04/handy-git-customizations</id><content type="html" xml:base="https://davedelong.com/blog/2023/03/04/handy-git-customizations/"><![CDATA[<p>Git is complicated, and it seems like everyone has a set of customizations they use to try and make it easier to use. Sometimes it aliases for clarifying what exactly <code class="language-plaintext highlighter-rouge">git checkout</code> will do. Sometimes it’s a pre-built <code class="language-plaintext highlighter-rouge">~/.gitconfig</code> file that has lots of tweaks and display customizations and options configured.</p>

<p>Over the years I’ve built up two that I use extremely frequently: <code class="language-plaintext highlighter-rouge">git ui</code> and <code class="language-plaintext highlighter-rouge">git identity</code>.</p>

<h2 id="git-ui">Git UI</h2>

<p>There are a number of Git clients out there, and in my opinion <a href="https://git-fork.com">Fork</a> is the best one. I’ve been using it for several years and LOVE it. At one point I even advocated <a href="https://github.com/fork-dev/Tracker/issues/562">that it shouldn’t be free</a> because good apps deserve to earn money.</p>

<p>Regardless of which Git app you use, I frequently find myself trolling around the command line doing stuff (cloning, pulling, etc) and then will come up against a task I want to do that, frankly, would be easier in an app. Interactive file staging is one such example. Enter <code class="language-plaintext highlighter-rouge">git ui</code>.</p>

<p>I created a file called <code class="language-plaintext highlighter-rouge">git-ui.zsh</code> and tossed it in my <code class="language-plaintext highlighter-rouge">$PATH</code> (<code class="language-plaintext highlighter-rouge">~/Applications</code> is where I happen to have it). This file is pretty simple:</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">GIT_FOLDER</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>git rev-parse <span class="nt">--show-toplevel</span> 2&gt;/dev/null<span class="si">)</span><span class="s2">"</span>
<span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"-r"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span>find <span class="s2">"</span><span class="nv">$GIT_FOLDER</span><span class="s2">"</span> <span class="nt">-name</span> .git <span class="nt">-execdir</span> open <span class="nt">-a</span> <span class="s2">"</span><span class="nv">$EDITOR_GIT</span><span class="s2">"</span> <span class="nb">.</span> <span class="se">\;</span>
<span class="k">else
    if</span> <span class="o">[</span> <span class="nt">-e</span> <span class="s2">"</span><span class="nv">$GIT_FOLDER</span><span class="s2">/.git"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
        </span><span class="nb">echo</span> <span class="s2">"Opening </span><span class="nv">$GIT_FOLDER</span><span class="s2"> in </span><span class="si">$(</span><span class="nb">basename</span> <span class="nv">$EDITOR_GIT</span><span class="si">)</span><span class="s2">"</span>
        open <span class="nt">-a</span> <span class="s2">"</span><span class="nv">$EDITOR_GIT</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$GIT_FOLDER</span><span class="s2">"</span>
    <span class="k">else
        </span><span class="nb">echo</span> <span class="s2">"Could not find git repository in </span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2"> or any parent"</span>
    <span class="k">fi
fi</span>
</code></pre></div></div>

<p>As part of the setup, this script relies on an environment variable I defined in my <code class="language-plaintext highlighter-rouge">~/.zshrc</code> called <code class="language-plaintext highlighter-rouge">EDITOR_GIT</code>:</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">EDITOR_GIT</span><span class="o">=</span><span class="s2">"/Applications/Fork.app"</span>
</code></pre></div></div>

<p>(I have several other <code class="language-plaintext highlighter-rouge">EDITOR_...</code> values defined, such as <code class="language-plaintext highlighter-rouge">EDITOR_MD</code> for markdown files, <code class="language-plaintext highlighter-rouge">EDITOR_PLIST</code> for property lists, etc. Then I’ve built up other commands for dealing with markdown and plist files specifically. This one uses <code class="language-plaintext highlighter-rouge">EDITOR_GIT</code> for working with git repositories.)</p>

<p><code class="language-plaintext highlighter-rouge">git-ui.zsh</code> first uses <code class="language-plaintext highlighter-rouge">git rev-parse</code> to locate the top-level directory of whatever git repository you happen to be in. For example, if your git repo is cloned to <code class="language-plaintext highlighter-rouge">~/Code/MyRepo</code>, then it will return <code class="language-plaintext highlighter-rouge">/Users/yourname/Code/MyRepo</code>, no matter which sub-directory of the repo you might be <code class="language-plaintext highlighter-rouge">cd</code>‘d into. This value gets stashed into the <code class="language-plaintext highlighter-rouge">GIT_FOLDER</code> variable.</p>

<p>Then, if you passed a <code class="language-plaintext highlighter-rouge">-r</code> flag to this command (<code class="language-plaintext highlighter-rouge">git ui -r</code>), it recursively finds all <code class="language-plaintext highlighter-rouge">.git</code> folders inside your git repository and asks them to open using your <code class="language-plaintext highlighter-rouge">$EDITOR_GIT</code> app. Otherwise, it looks for <code class="language-plaintext highlighter-rouge">.git</code> folder inside your repository root and, if it finds it, asks your <code class="language-plaintext highlighter-rouge">$EDITOR_GIT</code> to open up that folder. (<code class="language-plaintext highlighter-rouge">open</code> is a built-in command, and <code class="language-plaintext highlighter-rouge">open -a</code> means “Open the passed-in stuff using the specified application”)</p>

<p>By itself this script is cool, but when I also add an alias to my <code class="language-plaintext highlighter-rouge">~/.gitconfig</code>, it gets better:</p>

<pre><code class="language-plain">[alias]
	ui = !sh git-ui.zsh
</code></pre>

<p>(i.e. the <code class="language-plaintext highlighter-rouge">ui</code> subcommand is equivalent to invoking the <code class="language-plaintext highlighter-rouge">git-ui.zsh</code> script)</p>

<p>The end result is that I can be anywhere inside my git repo in Terminal and, upon realizing I need to do anything non-trivial, can type <code class="language-plaintext highlighter-rouge">git ui</code> to immediately pop up the current repository in my Git app-of-choice. I can also do <code class="language-plaintext highlighter-rouge">git ui -r</code> to open up all repositories in the current folder; I use this when I’ve got a folder of several related cloned repositories that I work with together.</p>

<h2 id="git-identity">Git Identity</h2>

<p>The other main customization I’ve created is the <code class="language-plaintext highlighter-rouge">identity</code> command. Many developers have multiple Github accounts: they have a personal one for their own projects and a work one that is associated with their work for their current employer (for example). The problem arises when want to work with multiple repositories from the same host (github.com) but under <em>different</em> accounts. You want to work with Repository A under your personal account, and Repository B with your work account.</p>

<p>There are weird hacks you can do to your <code class="language-plaintext highlighter-rouge">~/.ssh/config</code> file and custom hosts to make this work, but it’s a bit arcane, kind of weird to set up, and difficult to remember how it works. Fortunately, there’s a better way.</p>

<p>Git has a way to customize the command it uses to connect via SSH to the remote server, called <a href="https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresshCommand"><code class="language-plaintext highlighter-rouge">core.sshCommand</code></a>. If you change this, you can alter the way that <code class="language-plaintext highlighter-rouge">git</code> pushes and pulls from remotes. And since this is a configuration value, it can be specific on a per-repository basis.</p>

<p>What you want is to end up with an <code class="language-plaintext highlighter-rouge">sshCommand</code> that looks like this in your repository’s <code class="language-plaintext highlighter-rouge">.git/config</code> file:</p>

<pre><code class="language-plain">[core]
	sshCommand = ssh -i ~/.ssh/id_myworksshkey -F /dev/null -o 'IdentitiesOnly yes'
</code></pre>

<p>If you have this, then pushes and pulls to the repository will <em>only</em> use your <code class="language-plaintext highlighter-rouge">id_myworksshkey</code> identity; it won’t try to fall back to another identity or the keychain. And since this setup is a bit awkward to remember, we can make an alias to simplify it in our <code class="language-plaintext highlighter-rouge">~/.gitconfig</code> file:</p>

<pre><code class="language-plain">[alias]
	identity = "!f() { git config core.sshCommand \"ssh -i $1  -F /dev/null -o 'IdentitiesOnly yes'\"; }; f"
</code></pre>

<p>Now I can create a repo and run <code class="language-plaintext highlighter-rouge">git identity ~/.ssh/id_myworksshkey</code> to configure it with the right identity for pushing and pulling!</p>

<p>(Side note: not all Git apps will correctly use the specified <code class="language-plaintext highlighter-rouge">sshCommand</code> for a repo, but Fork does!)</p>

<hr />

<p>What handy <code class="language-plaintext highlighter-rouge">git</code> customizations have you come up with?</p>]]></content><author><name></name></author><category term="git" /><category term="tips" /><category term="devtools" /><summary type="html"><![CDATA[Git is complicated, and it seems like everyone has a set of customizations they use to try and make it easier to use. Sometimes it aliases for clarifying what exactly git checkout will do. Sometimes it’s a pre-built ~/.gitconfig file that has lots of tweaks and display customizations and options configured.]]></summary></entry><entry><title type="html">Adventures in Advent of Code</title><link href="https://davedelong.com/blog/2022/12/03/adventures-in-advent-of-code/" rel="alternate" type="text/html" title="Adventures in Advent of Code" /><published>2022-12-03T00:00:00+00:00</published><updated>2022-12-03T00:00:00+00:00</updated><id>https://davedelong.com/blog/2022/12/03/adventures-in-advent-of-code</id><content type="html" xml:base="https://davedelong.com/blog/2022/12/03/adventures-in-advent-of-code/"><![CDATA[<p>I’ve been participating regularly in <a href="https://adventofcode.com">Advent of Code</a> for the past couple of years. It’s one of the highlights of my holiday season. The puzzles are fun, the stories are appropriately ridiculous, and it’s a neat way for me to keep the cobwebs brushed off some of the things I learned years ago that I don’t regularly use. Every year there are puzzles that take me a couple of minutes to solve, and puzzles that take me hours: I will forever curse the Intcode puzzles from 2019.</p>

<p><a href="https://adventofcode.com/2022/day/3">Last night’s puzzle</a> was something new. The problem itself was pretty straight-forward (finding values that are common in multiple collections), but it resulted in a 45-minute debugging session that culminated in finding a bug in Swift’s implementation of <a href="https://developer.apple.com/documentation/swift/set/intersection(_:)-6uts9"><code class="language-plaintext highlighter-rouge">Set.intersection(_:)</code></a>.</p>

<p>The nature of last night’s problem was that, when I had a bunch of inputs, I could expect that there was only a single common element between all of them. After getting lucky and solving the problem correctly, I started <a href="https://en.wikipedia.org/wiki/Code_golf">golfing</a> my code to make it terser. That’s when I started noticing something odd.</p>

<p>My initial version of the code looked something like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">firstGroup</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">...</span>
<span class="k">let</span> <span class="nv">secondGroup</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">...</span>
<span class="k">let</span> <span class="nv">thirdGroup</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">...</span>

<span class="k">let</span> <span class="nv">uniqueLettersInFirstGroup</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="n">firstGroup</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">uniqueLettersInSecondGroup</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="n">secondGroup</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">uniqueLettersInThirdGroup</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="n">thirdGroup</span><span class="p">)</span>

<span class="k">let</span> <span class="nv">commonLetters</span> <span class="o">=</span> <span class="n">uniqueLettersInFirstGroup</span><span class="o">.</span><span class="nf">intersection</span><span class="p">(</span><span class="n">uniqueLettersInSecondGroup</span><span class="p">)</span><span class="o">.</span><span class="nf">intersection</span><span class="p">(</span><span class="n">uniqueLettersInThirdGroup</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">commonLetter</span> <span class="o">=</span> <span class="n">commonLetters</span><span class="o">.</span><span class="n">first</span><span class="o">!</span> <span class="c1">// safe to unwrap, because if this crashes the input is bad</span>
<span class="c1">// ... do processing with the common letter</span>
</code></pre></div></div>

<p>This worked great, but it’s also a lot of code. So I started combining things:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">firstGroup</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">...</span>
<span class="k">let</span> <span class="nv">secondGroup</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">...</span>
<span class="k">let</span> <span class="nv">thirdGroup</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">...</span>

<span class="k">let</span> <span class="nv">commonLetters</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="n">firstGroup</span><span class="p">)</span><span class="o">.</span><span class="nf">intersection</span><span class="p">(</span><span class="n">secondGroup</span><span class="p">)</span><span class="o">.</span><span class="nf">intersection</span><span class="p">(</span><span class="n">thirdGroup</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">commonLetter</span> <span class="o">=</span> <span class="n">commonLetters</span><span class="o">.</span><span class="n">first</span><span class="o">!</span>
</code></pre></div></div>

<p>Eventually I decided to make an extension, since this sort of algorithm (“find what’s in common between these groups of things”) is a pretty normal thing to encounter in Advent of Code:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Collection</span> <span class="k">where</span> <span class="kt">Element</span><span class="p">:</span> <span class="kt">Collection</span><span class="p">,</span> <span class="kt">Element</span><span class="o">.</span><span class="kt">Element</span><span class="p">:</span> <span class="kt">Hashable</span> <span class="p">{</span>

    <span class="k">var</span> <span class="nv">commonElements</span><span class="p">:</span> <span class="kt">Set</span><span class="o">&lt;</span><span class="kt">Element</span><span class="o">.</span><span class="kt">Element</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="c1">// intersect all the elements</span>
        <span class="c1">// return the final intersection</span>
    <span class="p">}</span>

<span class="p">}</span>

<span class="k">let</span> <span class="nv">firstGroup</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">...</span>
<span class="k">let</span> <span class="nv">secondGroup</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">...</span>
<span class="k">let</span> <span class="nv">thirdGroup</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">...</span>

<span class="k">let</span> <span class="nv">commonLetters</span> <span class="o">=</span> <span class="p">[</span><span class="n">firstGroup</span><span class="p">,</span> <span class="n">secondGroup</span><span class="p">,</span> <span class="n">thirdGroup</span><span class="p">]</span><span class="o">.</span><span class="n">commonElements</span>
<span class="k">let</span> <span class="nv">commonLetter</span> <span class="o">=</span> <span class="n">commonLetters</span><span class="o">.</span><span class="n">first</span><span class="o">!</span>
</code></pre></div></div>

<p>It was about this point that I started noticing something weird: every time I ran my code, I’d get a different answer.</p>

<p>This is not how Advent of Code works. It is very <a href="https://en.wikipedia.org/wiki/Deterministic_algorithm">deterministic</a>: for each input, there is a single correct output. And as luck would have it, I’d already found the correct output. What I was getting now as everything <em>except</em> correct.</p>

<h2 id="questioning-the-nature-of-my-existence">Questioning The Nature Of My Existence</h2>

<p>After putting in a <a href="https://medium.com/supernova-invention-park/the-caveman-debugging-ab8f7151415f">hefty number of log statements</a>, I realized: my set of “common elements” would sometimes have <em>more than one element in it</em>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Starting with gnmCjzwnmCPTPhBwPjzBgqPjllJJSWlhfhQDSrpJRhDSlfJl
Intersecting rLHNHrLHVNbVHMMctZFHsbcsDSDWpSDSGfSRsRWSRllfGSSG
Intersecting NNtdMVrLNdZNvLvLZrzCndqBgwwPmwgjggBn
ERROR
Common Elements??? - ["j", "r", "B", "m", "z", "n", "q", "w", "g", "C", "P"]
</code></pre></div></div>

<p>As a general principle that’s fine. <code class="language-plaintext highlighter-rouge">["apple", "pear", "papaya"].commonElements</code> would have multiple things in it: <code class="language-plaintext highlighter-rouge">["a", "p"]</code>. But in this case it was not what I wanted, because I knew that I should only be getting a single element in common (due to the nature of this particular Advent of Code puzzle).</p>

<p><details>
    <summary>Why I was getting a different answer every time the code ran?</summary>

<blockquote><p>This problem of "getting many things back when I was only expecting one" also fully explains why I kept getting different results every time I ran the code. The <code>Set</code> of common elements was returning many things, but I was asking for the <code>.first</code> element in the Set. Sets, by their very nature, do not have a specific ordering, so when I ran the code over and over again, the "first" thing would be different each time. That meant that, each time, I'd get a different final result.</p></blockquote>
</details></p>

<p>After staring at my code for a good 10 minutes, I did the sane thing: I asked for help.</p>

<p><img src="/files/screen-shot-20221203-at-95307-am.png" alt="Screenshot of a slack post where I state &quot;man i’ve got something really weird going on with my code. i’m getting a proper chunk of 3 strings, but when i try to do the part 2 logic on them, it’s saying there are 11 elements in common between them. It’s consistently ignoring the second value&quot;" /></p>

<p>Some of the others who were also up doing Advent of Code in this particular forum graciously popped into the thread and started doing what I was doing: digging apart every single line of code, trying to identify assumptions or gaps in logic. The questions started simple: “do you have typos?”. Then they started digging in to my <code class="language-plaintext highlighter-rouge">commonElements</code> implementation. “What’s this extension? What if you use <code class="language-plaintext highlighter-rouge">dropFirst()</code>? Are there assumptions that fail because this will be a <code class="language-plaintext highlighter-rouge">SubSequence</code> instead of an <code class="language-plaintext highlighter-rouge">Array</code>? Are you using the <a href="https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Chunked.swift">Swift Algorithms</a> version of <code class="language-plaintext highlighter-rouge">.chunks(ofCount:)</code> or your own?”. Each question and it’s corresponding answer confirmed that, by all accounts, the code looked correct.</p>

<p>Then on a whim I asked:</p>

<blockquote>
  <p>is it possible there’s a bug in <code class="language-plaintext highlighter-rouge">Set</code>? 😛</p>
</blockquote>

<p>Now, the likelihood of this actually being the case is very very very very small. So many people are using <code class="language-plaintext highlighter-rouge">Set</code>, a <em>fundamental</em> collection type, every single day that idea of there being a bug in its implementation–especially in something as important as <a href="https://en.wikipedia.org/wiki/Intersection_%28set_theory%29">set intersection</a>–seemed laughably absurd.</p>

<blockquote>
  <p>WTF. Did Set intersection break and we’re just noticing?</p>
</blockquote>

<p>We tried different versions of Xcode. We even considered manually downloading Swift:</p>

<blockquote>
  <p>Try downloading a recent swift toolchain</p>
</blockquote>

<p>And yet, the code was still broken. And it <em>continued</em> to be broken when pulled out into a Swift playground, removing all of my fancy extensions:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="s">"gnmCjzwnmCPTPhBwPjzBgqPjllJJSWlhfhQDSrpJRhDSlfJl"</span>
<span class="k">let</span> <span class="nv">b</span> <span class="o">=</span> <span class="s">"rLHNHrLHVNbVHMMctZFHsbcsDSDWpSDSGfSRsRWSRllfGSSG"</span>
<span class="k">let</span> <span class="nv">c</span> <span class="o">=</span> <span class="s">"NNtdMVrLNdZNvLvLZrzCndqBgwwPmwgjggBn"</span>

<span class="k">var</span> <span class="nv">s</span> <span class="o">=</span> <span class="kt">Set</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="nf">formIntersection</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="nf">formIntersection</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="c1">// ["C", "q", "m", "r", "j", "n", "z", "g", "w", "P", "B"]</span>
</code></pre></div></div>

<p>At this point, we had to conclude that something really strange was going on. Then one poster hit on an idea:</p>

<blockquote>
  <p>Yikes, try converting a b and c to sets before intersecting</p>
</blockquote>

<p>This change was simple. Instead of <code class="language-plaintext highlighter-rouge">s.formIntersection(a)</code>, I changed it to <code class="language-plaintext highlighter-rouge">set.formIntersection(Set(a))</code>. The change here is that instead of using the generic <a href="https://github.com/apple/swift/blob/main/stdlib/public/core/Set.swift#L955">“find things in common with this other <em>sequence</em> of characters”</a>, I was now using the method <a href="https://github.com/apple/swift/blob/main/stdlib/public/core/Set.swift#L1242">“find things in common with this other <code class="language-plaintext highlighter-rouge">Set</code>”</a>. These have different implementations because, due to the nature of how sets work, the second can be done much more “cheaply” than the first.</p>

<blockquote>
  <p>I think you found a (known) bug</p>
</blockquote>

<p>So I tried it, and suddenly my code started working. The one who suggested this then dug up this pull request on the Swift repo: <a href="https://github.com/apple/swift/pull/59422">PR #59422: Fix handling of duplicate items in generic Set.intersection</a>. It turns out, there <em>was</em> a bug in <code class="language-plaintext highlighter-rouge">Set.intersection(_:)</code>, but it had only been discovered this past June, and the fix only applies to macOS Ventura and later (my machine is running Monterey still). The scope of the bug is fairly limited: it only showed up if you were using the general intersection method, and the sequence had “exactly as many duplicate items as items missing from <code class="language-plaintext highlighter-rouge">self</code>”. As it turned out, Advent of Code happened to provide me with exactly the right input to hit this multiple times.</p>

<p>In the end, <a href="https://github.com/davedelong/AOC/blob/main/Sources/AOCCore/Data%20Structures/Collection.swift#L366-L384">my workaround was simple</a>: I could simply make sure I pass in <code class="language-plaintext highlighter-rouge">Set(item)</code> instead of just <code class="language-plaintext highlighter-rouge">item</code>. But the adventure of digging this deep into my code, questioning assumption after assumption, and coming up with ways to test those assumptions was quite exhilarating.</p>

<p>And it proves that maybe… <em>just maybe</em>… it really <em>is</em> a bug in the standard library.</p>

<hr />

<p>Edit: An earlier version of this post claimed the bug was not fixed yet. This is incorrect. The bug <em>is</em> fixed in Swift 5.7, but my computer is running macOS Monterey (12.6) and thus using an earlier version of Swift. I have since confirmed that the code works as expected on macOS Ventura.</p>]]></content><author><name></name></author><category term="swift" /><category term="advent of code" /><summary type="html"><![CDATA[I’ve been participating regularly in Advent of Code for the past couple of years. It’s one of the highlights of my holiday season. The puzzles are fun, the stories are appropriately ridiculous, and it’s a neat way for me to keep the cobwebs brushed off some of the things I learned years ago that I don’t regularly use. Every year there are puzzles that take me a couple of minutes to solve, and puzzles that take me hours: I will forever curse the Intcode puzzles from 2019.]]></summary></entry><entry><title type="html">My First App</title><link href="https://davedelong.com/blog/2022/08/31/my-first-app/" rel="alternate" type="text/html" title="My First App" /><published>2022-08-31T00:00:00+00:00</published><updated>2022-08-31T00:00:00+00:00</updated><id>https://davedelong.com/blog/2022/08/31/my-first-app</id><content type="html" xml:base="https://davedelong.com/blog/2022/08/31/my-first-app/"><![CDATA[<p>I grew up around technology, so I don’t have good memories of when I really “started” getting in to programming. When I was around 14, I picked up HTML and started playing around with that. I learned to make simple graphics in <a href="https://en.wikipedia.org/wiki/AppleWorks">AppleWorks</a> that I’d embed in those pages.</p>

<p>But, I do remember my first “real” app. I wrote it during the 2001–2002 school year, when I was taking trigonometry in high school. I had a TI-83+ graphing calculator and a smattering of games to play on it, but one day early on it occurred to me that I could, in theory, make my calculator do my homework for me. My homework usually consisted of endless applications of the <a href="https://en.wikipedia.org/wiki/Law_of_sines">Law of sines</a> and <a href="https://en.wikipedia.org/wiki/Law_of_cosines">Law of cosines</a>, and the constant repetition seemed like an ideal thing to make <em>something else</em> do for me.</p>

<p>So, I dragged out the manual that came with my calculator and started creating a program that would ask for three pieces of information about a triangle (some combination of angles and sides) and would give me the rest of the information.</p>

<p>I iterated on this program a LOT. I remember working on my bedroom floor with graph paper, sketching out all of the interaction and (what I later learned was called) control flow. It was a lot to keep in my 15-year-old brain, and the paper helped a lot. The program was enormously helpful, but had the unfortunate side effect of making me learn the material <em>better</em> because of all the time I spent picking apart algorithms, than if I had just done the homework myself.</p>

<p>A couple years ago, I rediscovered my calculator in a box of old stuff. On a whim, I bought a <a href="https://amzn.to/3Txhrbi">data cable</a> off Amazon, put in fresh batteries, and Lo And Behold, there was my program!</p>

<p>This is a really important program to me. The code itself is straight-forward (lots of <code class="language-plaintext highlighter-rouge">goto</code> statements, asking for input, and then doing math and printing the result), but what it represents is so much more. First, it triggered a deeper interest in math, leading me to <a href="/blog/2018/05/01/deriving-a-new-formula/">go off and derive a new formula related to solving triangles</a>. But more importantly, it was the catalyst to get me really interested in programming: I had a need, and I discovered that I could <em>make something</em> to satisfy that need and make my life (and the lives of my classmates, who also got copies of this program) so much easier. That core epiphany has stayed with me and continues to drive me, two decades later.</p>

<p>So if you’re curious, I’ve put the code online. I also translated it into C to make it more readable, and so you can run it on your computer:</p>

<p><a href="https://github.com/davedelong/triangle">https://github.com/davedelong/triangle</a></p>]]></content><author><name></name></author><category term="code" /><summary type="html"><![CDATA[I grew up around technology, so I don’t have good memories of when I really “started” getting in to programming. When I was around 14, I picked up HTML and started playing around with that. I learned to make simple graphics in AppleWorks that I’d embed in those pages.]]></summary></entry><entry><title type="html">Conditional Compilation, Part 4: Deployment Targets</title><link href="https://davedelong.com/blog/2022/05/15/conditional-compilation-part-4-deployment-targets/" rel="alternate" type="text/html" title="Conditional Compilation, Part 4: Deployment Targets" /><published>2022-05-15T00:00:00+00:00</published><updated>2022-05-15T00:00:00+00:00</updated><id>https://davedelong.com/blog/2022/05/15/conditional-compilation-part-4-deployment-targets</id><content type="html" xml:base="https://davedelong.com/blog/2022/05/15/conditional-compilation-part-4-deployment-targets/"><![CDATA[<p>Recently I was thinking about the idea I’d posted on <a href="/blog/2021/10/09/simplifying-backwards-compatibility-in-swift/">simplifying backwards compatibility in Swift</a>, and was also thinking about some of the principles of kindness that I wrote about in my article on <a href="/articles/api-design/">API design</a>.</p>

<p>As I was mulling these over, an idea occurred to me: I can improve the process of <em>removing</em> backwards compatibility shims by using conditional compilation to remind me when they’re no longer necessary!</p>

<h2 id="the-premise">The Premise</h2>

<p>SwiftUI was introduced in iOS 13/macOS 10.15, and we commonly refer to that release as “SwiftUI 1.0”. Over the intervening years, we’ve had SwiftUI 2.0 and SwiftUI 3.0. Each release has added more features, as well as provided additional opportunities for app developers to back-deploy features as they’re building apps and adopting new APIs. In my blog post on backwards compatibility, I introduced the idea of a <code class="language-plaintext highlighter-rouge">Backport</code> type to serve as a namespace for these sorts of compatibility shims.</p>

<p>But … when those shims are no longer necessary, how do we remember that we should take them out? It’s really easy to forget that they’re there and allow unnecessary cruft to build up in a codebase over time.</p>

<p>Wouldn’t it be cool if we could use the compiler to help us know when the code wasn’t necessary anymore?</p>

<p>We’ve seen in previous posts how we can provide “compilation conditions” to use with <code class="language-plaintext highlighter-rouge">#if</code> statements in our codebase, like <code class="language-plaintext highlighter-rouge">#if BUILDING_FOR_DEVICE</code>, <code class="language-plaintext highlighter-rouge">#if BUILDING_FOR_APP_EXTENSION</code>, and so on. We’re going to come up with a way to that will allow us to specify <code class="language-plaintext highlighter-rouge">#if TARGETING_SWIFTUI_1</code> or <code class="language-plaintext highlighter-rouge">#if TARGETING_SWIFTUI_2</code> in our code, and use that to leave messages to our future selves.</p>

<h2 id="build-setting-transformations">Build Setting Transformations</h2>

<p>Every app you build in Xcode has a “deployment target”, which is the minimum operating system version you allow your app to run on. This value is defined by the <code class="language-plaintext highlighter-rouge">MACOSX_DEPLOYMENT_TARGET</code> build setting (or <code class="language-plaintext highlighter-rouge">IPHONEOS_DEPLOYMENT_TARGET</code>, <code class="language-plaintext highlighter-rouge">TVOS_DEPLOYMENT_TARGET</code>, or <code class="language-plaintext highlighter-rouge">WATCHOS_DEPLOYMENT_TARGET</code> settings, depending on the platform you’re targeting). The value of this build setting is the operating system version number, like <code class="language-plaintext highlighter-rouge">10.15</code> or <code class="language-plaintext highlighter-rouge">8.3</code> or whatever.</p>

<p>We can append this value to other build settings via substitution, but we quickly run in to some issues:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>_SWIFTUI_VERSION_10.15 = 1
_SWIFTUI_VERSION_11.0 = 2
_SWIFTUI_VERSION_12.0 = 3

_SWIFTUI_VERSION = $(_SWIFTUI_VERSION_$(MACOSX_DEPLOYMENT_TARGET))
</code></pre></div></div>

<p>If we do this, we get a compilation error! As it turns out, <code class="language-plaintext highlighter-rouge">.</code> is not a legal value to put into build setting names. Fortunately, we can <em>transform</em> the build setting value before substituting it.</p>

<p>Transformation operators are appended to the build setting name, after a <code class="language-plaintext highlighter-rouge">:</code> character. The list of supported operators is below<a href="#fn1" name="ret1">¹</a>.</p>

<table>
<tr>
	<th>Operator</th>
	<th>Transformation</th>
</tr>
<tr>
	<td><code>identifier</code></td>
	<td>A C identifier representation suitable for use in source code.</td>
</tr>
<tr>
	<td><code>c99extidentifier</code></td>
	<td>Like <code>identifier</code>, but with support for extended characters allowed by C99.</td>
</tr>
<tr>
	<td><code>rfc1034identifier</code></td>
	<td>A representation suitable for use in a DNS name.</td>
</tr>
<tr>
	<td><code>quote</code></td>
	<td>A representation suitable for use as a shell argument.</td>
</tr>
<tr>
	<td><code>lower</code></td>
	<td>A lowercase representation.</td>
</tr>
<tr>
	<td><code>upper</code></td>
	<td>An uppercase representation.</td>
</tr>
<tr>
	<td><code>standardizepath</code></td>
	<td>The equivalent of calling <code>-stringByStandardizingPath</code> on the string.</td>
</tr>
<tr>
	<td><code>base</code></td>
	<td>The base name of a path - the last path component with any extension removed.</td>
</tr>
<tr>
	<td><code>dir</code></td>
	<td>The directory portion of a path.</td>
</tr>
<tr>
	<td><code>file</code></td>
	<td>The file portion of a path.</td>
</tr>
<tr>
	<td><code>suffix</code></td>
	<td>The extension of a path including the <code>.</code> divider.</td>
</tr>
</table>

<p>And, these operators can be chained by concatenating another <code class="language-plaintext highlighter-rouge">:</code> and operator name. We’ll use these transformations to come up with a better format for our deployment target value.</p>

<p>If we look at the value, such as <code class="language-plaintext highlighter-rouge">10.15</code>, we’ll see that it kind of looks like a file name: a file named <code class="language-plaintext highlighter-rouge">10</code> with an extension of <code class="language-plaintext highlighter-rouge">15</code>. We can <del>abuse</del> leverage some of the file-based operators to extract the major and minor values:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DEPLOYMENT_TARGET_NUMBER_MAJOR = $(MACOSX_DEPLOYMENT_TARGET:base)
DEPLOYMENT_TARGET_NUMBER_MINOR = $(MACOSX_DEPLOYMENT_TARGET:suffix:c99extidentifier)
DEPLOYMENT_TARGET_NUMBER = $(DEPLOYMENT_TARGET_NUMBER_MAJOR)$(DEPLOYMENT_TARGET_NUMBER_MINOR)
</code></pre></div></div>

<p>If we do this, we end up with the <code class="language-plaintext highlighter-rouge">DEPLOYMENT_TARGET</code> defined as <code class="language-plaintext highlighter-rouge">10_15</code>. Unfortunately, we can’t use <code class="language-plaintext highlighter-rouge">c99extidentifier</code> directly, because that <em>strips</em> the leading number of the deployment target. So we resort to this “file” approach (getting the “basename” of the value and its “suffix”, and then using <code class="language-plaintext highlighter-rouge">c99extidentifier</code> to turn the <code class="language-plaintext highlighter-rouge">.</code> into a <code class="language-plaintext highlighter-rouge">_</code>) to get our transformed value.</p>

<h2 id="the-setup">The Setup</h2>

<p>Now that we can transform our deployment target into a safe value, we can build up our settings:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>_SWIFTUI_VERSION_10_15 = 1
_SWIFTUI_VERSION_11_0 = 2
_SWIFTUI_VERSION_12_0 = 3

_SWIFTUI_VERSION = $(_SWIFTUI_VERSION_$(DEPLOYMENT_TARGET_NUMBER))
</code></pre></div></div>

<p>For completeness, we can define these values for every platform:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>_PLATFORM =
_PLATFORM[sdk=mac*] = MACOSX
_PLATFORM[sdk=iphone*] = IPHONEOS
_PLATFORM[sdk=appletv*] = TVOS
_PLATFORM[sdk=watch*] = WATCHOS

// Sanitize the numeric deployment target value
_DEPLOYMENT_TARGET = $($(_PLATFORM)_DEPLOYMENT_TARGET)
DEPLOYMENT_TARGET_NUMBER_MAJOR = $(_DEPLOYMENT_TARGET:base)
DEPLOYMENT_TARGET_NUMBER_MINOR = $(_DEPLOYMENT_TARGET:suffix:c99extidentifier)
DEPLOYMENT_TARGET_NUMBER = $(DEPLOYMENT_TARGET_NUMBER_MAJOR)$(DEPLOYMENT_TARGET_NUMBER_MINOR)

// The naming scheme is "_SWIFTUI_VERSION_" + platform name + "_" + os version
_SWIFTUI_VERSION_MACOSX_10_15 = 1
_SWIFTUI_VERSION_MACOSX_11_0 = 2
_SWIFTUI_VERSION_MACOSX_12_0 = 3

_SWIFTUI_VERSION_IPHONEOS_13_0 = 1
_SWIFTUI_VERSION_IPHONEOS_14_0 = 2
_SWIFTUI_VERSION_IPHONEOS_15_0 = 3

_SWIFTUI_VERSION_TVOS_13_0 = 1
_SWIFTUI_VERSION_TVOS_14_0 = 2
_SWIFTUI_VERSION_TVOS_15_0 = 3

_SWIFTUI_VERSION_WATCHOS_6_0 = 1
_SWIFTUI_VERSION_WATCHOS_7_0 = 2
_SWIFTUI_VERSION_WATCHOS_8_0 = 3

// Get the SwiftUI version based on the platform and deployment target
_SWIFTUI_VERSION = $(_SWIFTUI_VERSION_$(_PLATFORM)_$(DEPLOYMENT_TARGET_NUMBER))

// Define the values to be used as compilation conditions, based on the SwiftUI version
_SWIFTUI_1 = TARGETING_SWIFTUI_1 TARGETING_SWIFTUI_2 TARGETING_SWIFTUI_3
_SWIFTUI_2 = TARGETING_SWIFTUI_2 TARGETING_SWIFTUI_3
_SWIFTUI_3 = TARGETING_SWIFTUI_3

SWIFTUI = $(_SWIFTUI_$(_SWIFTUI_VERSION))

SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(SWIFTUI)
</code></pre></div></div>

<p>Whew, that’s a lot! But, we’ve got something pretty cool now. Let’s put it to use!</p>

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

<p>With values like <code class="language-plaintext highlighter-rouge">TARGETING_SWIFTUI_1</code> or <code class="language-plaintext highlighter-rouge">TARGETING_SWIFTUI_3</code> in the <code class="language-plaintext highlighter-rouge">SWIFT_ACTIVE_COMPILATION_CONDITIONS</code>, we can use them as part of <code class="language-plaintext highlighter-rouge">#if</code> conditionals:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Backport</span> <span class="k">where</span> <span class="kt">Content</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>

    <span class="cp">#if TARGETING_SWIFTUI_2 || TARGETING_SWIFTUI_1</span>
    <span class="c1">// we're deploying to macOS &lt; 12</span>
    <span class="kd">@ViewBuilder</span> <span class="kd">func</span> <span class="nf">badge</span><span class="p">(</span><span class="n">_</span> <span class="nv">count</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">if</span> <span class="k">#available</span><span class="p">(</span><span class="n">macOS</span> <span class="mi">12</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">content</span><span class="o">.</span><span class="nf">badge</span><span class="p">(</span><span class="n">count</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">content</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="cp">#else</span>
        <span class="cp">#error("We're only targeting SwiftUI 3+. Backporting `.badge(_:)` is unnecessary and should be removed.")</span>
    <span class="cp">#endif</span>

<span class="p">}</span>
</code></pre></div></div>

<p>Now as we adjust our deployment target, the active compilation conditions will change depending on the OS version (and platform) we’re targeting. If we move our deployment target up such that we’re no longer targeting SwiftUI 2 (ie, macOS 11.0, iOS 14, tvOS 14, or watchOS 7), then the compiler will stop building this <code class="language-plaintext highlighter-rouge">badge(_:)</code> method and instead will produce an error telling us to clean up the unnecessary code.</p>

<p>This screenshot shows what happens when we update our deployment target to macOS 12:</p>

<p><img src="/files/backporterror.png" alt="After adjusting the deployment target, our code now produces a compilation error." /></p>

<p>This does mean that the first time we change our deployment target, we’ll get a bunch of compilation errors. But given the nature of how <code class="language-plaintext highlighter-rouge">Backport</code> is implemented, this should be a relatively quick process to move past. (And of course, you’re welcome to use <code class="language-plaintext highlighter-rouge">#warning</code> instead of <code class="language-plaintext highlighter-rouge">#error</code>).</p>

<h2 id="shortcomings">Shortcomings</h2>

<p>There are a couple of small drawbacks with this specific approach.</p>

<p>First, every new SwiftUI version will need new values in your configuration file. As new SDKs come, there’s a small amount of bookkeeping necessary to make sure the various condition values get defined.</p>

<p>Second, if you’re targeting specific <em>minor</em> OS version (macOS 12.3, for example), then you also have to fill out more values for the SwiftUI versions. You could probably work around this by only keying off the <em>major</em> OS version number, but that’s a decision that’s dependent on your use-case and how far back you need to deploy.</p>

<p>Finally, using <code class="language-plaintext highlighter-rouge">#error</code> (as demonstrated above) means that the task of updating a deployment target now becomes a <em>little</em> tedious: you have to fix all of these build errors before continuing; adopting changes in a piecemeal fashion becomes more difficult (although this can be mitigated by using <code class="language-plaintext highlighter-rouge">#warning</code> instead).</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>When we write code, it’s always nice to leave things for future maintainers to guide them down the correct path and avoid pitfalls. This typically takes the form of comments, but with a bit of clever application we can use the compiler to help as well. This allows us to leave guideposts that can keep our code clean, or leave warnings and reminders. Maybe you want to see if a particular workaround is still necessary in a system framework? Leave a <code class="language-plaintext highlighter-rouge">#if</code> in your code like this that reminds you to check the next time you update your base SDK (<code class="language-plaintext highlighter-rouge">SDK_VERSION</code>). Working around a specific bug in Xcode? Leave a reminder for yourself based on the <code class="language-plaintext highlighter-rouge">XCODE_VERSION_MAJOR</code> (or <code class="language-plaintext highlighter-rouge">XCODE_VERSION_MINOR</code> or <code class="language-plaintext highlighter-rouge">XCODE_VERSION_ACTUAL</code>) to check if it’s still necessary. Maybe you want to remind yourself to revisit some code when your <code class="language-plaintext highlighter-rouge">MARKETING_VERSION</code> goes from <code class="language-plaintext highlighter-rouge">1.x</code> to <code class="language-plaintext highlighter-rouge">2.0</code>? Leave yourself a compiler note!</p>

<p>Your future self will thank you.</p>

<hr />

<p><a name="fn1">¹</a> - This table was taken from Matt Stevens’ blog post here: <a href="http://codeworkshop.net/posts/xcode-build-setting-transformations">http://codeworkshop.net/posts/xcode-build-setting-transformations</a>. <a href="#ret1">↑</a></p>]]></content><author><name></name></author><category term="swift" /><category term="xcode" /><category term="conditional-compilation" /><category term="devtools" /><summary type="html"><![CDATA[Recently I was thinking about the idea I’d posted on simplifying backwards compatibility in Swift, and was also thinking about some of the principles of kindness that I wrote about in my article on API design.]]></summary></entry><entry><title type="html">Simplifying Backwards Compatibility in Swift</title><link href="https://davedelong.com/blog/2021/10/09/simplifying-backwards-compatibility-in-swift/" rel="alternate" type="text/html" title="Simplifying Backwards Compatibility in Swift" /><published>2021-10-09T00:00:00+00:00</published><updated>2021-10-09T00:00:00+00:00</updated><id>https://davedelong.com/blog/2021/10/09/simplifying-backwards-compatibility-in-swift</id><content type="html" xml:base="https://davedelong.com/blog/2021/10/09/simplifying-backwards-compatibility-in-swift/"><![CDATA[<p>Every year as new OS and Swift versions are released, the question comes up over and over again: “how do I use this new thing while also supporting older versions?”. While we have a bunch of “availability” tools at our disposal (and I’ll be using them in this post), they always come across as somewhat <em>cumbersome</em>: we need to do inline checks, or we have conditional logic flow that obfuscates the intent of some of our code, and so on.</p>

<p>A little while ago I was thinking about this problem and came up with a technique that helps clarify some (but not all) aspects of this problem.</p>

<p>When I design APIs, I like to start from the end result and design backwards. I start with the question of “how do I want to use this?” or “what is the least-intrusive way to make this?”. Good APIs have minimal interfaces that allow clients to quickly solve their problems without unnecessarily burdening them with boilerplate or complicated configuration and control flow.</p>

<p>With this in mind, I came up with <code class="language-plaintext highlighter-rouge">Backport</code>.</p>

<p>The definition of <code class="language-plaintext highlighter-rouge">Backport</code> is trivial:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Backport</span><span class="o">&lt;</span><span class="kt">Content</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="k">let</span> <span class="nv">content</span><span class="p">:</span> <span class="kt">Content</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">content</span><span class="p">:</span> <span class="kt">Content</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="n">content</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>At first glance, this doesn’t look very useful; it’s a struct that holds a single value, and it doesn’t <em>do</em> anything. This is by design. <code class="language-plaintext highlighter-rouge">Backport</code> exists to serve as a holding space (namespace) for <em>shims</em>: the conditional code we <em>must</em> write in order to do proper availability checking. Let’s look at a specific example for how we can do this.</p>

<h2 id="backporting-methods">Backporting Methods</h2>

<p>In iOS 15, SwiftUI added some modifiers to describe badges on list rows and tab items:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">badge</span><span class="p">(</span><span class="n">_</span> <span class="nv">count</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">badge</span><span class="p">(</span><span class="n">_</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">Text</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">badge</span><span class="p">(</span><span class="n">_</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">LocalizedStringKey</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="n">badge</span><span class="o">&lt;</span><span class="kt">S</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">S</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="k">where</span> <span class="kt">S</span> <span class="p">:</span> <span class="kt">StringProtocol</span>
<span class="p">}</span>
</code></pre></div></div>

<p>These are really useful, but have the downside that, if you’re supporting iOS 14 or earlier, you get a compiler error if you try to use these directly:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">someTabView</span>
    <span class="o">.</span><span class="nf">badge</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="c1">// error: 'badge' is only available in iOS 15.0 or newer</span>
</code></pre></div></div>

<p>This is where <code class="language-plaintext highlighter-rouge">Backport</code> can help:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">backport</span><span class="p">:</span> <span class="kt">Backport</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span> <span class="p">{</span> <span class="kt">Backport</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">Backport</span> <span class="k">where</span> <span class="kt">Content</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@ViewBuilder</span> <span class="kd">func</span> <span class="nf">badge</span><span class="p">(</span><span class="n">_</span> <span class="nv">count</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">if</span> <span class="k">#available</span><span class="p">(</span><span class="n">iOS</span> <span class="mi">15</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">content</span><span class="o">.</span><span class="nf">badge</span><span class="p">(</span><span class="n">count</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">content</span> 
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here we’re doing a couple of things:</p>

<ol>
  <li>We’re saying that every <code class="language-plaintext highlighter-rouge">View</code> has a <code class="language-plaintext highlighter-rouge">.backport</code> property that returns a <code class="language-plaintext highlighter-rouge">Backport</code> which holds the view</li>
  <li>When a <code class="language-plaintext highlighter-rouge">Backport</code> holds a view, it gets this <code class="language-plaintext highlighter-rouge">badge(_ count: Int)</code> method.</li>
  <li>This method does the check to see if the SwiftUI version of <code class="language-plaintext highlighter-rouge">.badge()</code> is available. If it is, it passes the parameter on to the system implementation</li>
  <li>If the SwiftUI method is not available, it falls back (“backports”) to some other implementation, which is up to you. In the example above, it simply ignores the parameter and returns the un-modified view.</li>
</ol>

<p>This means that we can now do:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">someTabView</span>
    <span class="o">.</span><span class="n">backport</span><span class="o">.</span><span class="nf">badge</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="c1">// no error, and will have a badge if the app is running on iOS 15 or later</span>
</code></pre></div></div>

<h2 id="backporting-types">Backporting Types</h2>

<p>This idea of using <code class="language-plaintext highlighter-rouge">Backport</code> as a <em>namespace</em> can apply to types as well. iOS 15 also added a new kind of view, called <code class="language-plaintext highlighter-rouge">AsyncImage</code>. This is a view that can handle loading an image from an asynchronous source, like a URL. In many respects, it is similar to the popular <a href="https://github.com/SDWebImage/SDWebImage">SDWebImage</a> (and similar) package.</p>

<p>However, like with <code class="language-plaintext highlighter-rouge">.badge(…)</code>, if you try to use it while also supporting iOS 14, you’ll get a compiler error:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">AsyncImage</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">someImageURL</span><span class="p">)</span> <span class="c1">// error: 'AsyncImage' is only available in iOS 15.0 or newer</span>
</code></pre></div></div>

<p>Again, let’s start with what we want the final result to look like and use that to work towards an implementation:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">Backport</span><span class="o">.</span><span class="kt">AsyncImage</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">someImageURL</span><span class="p">)</span>
</code></pre></div></div>

<p>There are two ways we could implement this; I’ll show both. The first way is what you might expect, with a nested type:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// a same-type requirement of "Content == Any" is needed to help </span>
<span class="c1">// the compiler figure out what to do.</span>
<span class="c1">// Thanks to type inference, we won't need to actually type </span>
<span class="c1">// "Backport&lt;Any&gt;"; the compiler can figure it out when we </span>
<span class="c1">// type `Backport.AsyncImage…`</span>
<span class="c1">//</span>
<span class="c1">// We'd only need to specify the `&lt;Any&gt;` parameter if a different</span>
<span class="c1">// extension also declared a nested `AsyncImage` symbol.</span>

<span class="kd">extension</span> <span class="kt">Backport</span> <span class="k">where</span> <span class="kt">Content</span> <span class="o">==</span> <span class="kt">Any</span> <span class="p">{</span>

    <span class="kd">struct</span> <span class="kt">AsyncImage</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">url</span><span class="p">:</span> <span class="kt">URL</span><span class="p">?</span>

        <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">#available</span><span class="p">(</span><span class="n">iOS</span> <span class="mi">15</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">SwiftUI</span><span class="o">.</span><span class="kt">AsyncImage</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="kt">MyCustomAsyncImage</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

<span class="p">}</span>
</code></pre></div></div>

<p>This approach works well when you have a new type that has a single main entry point for configuration (ie, some sort of designated initializer). However <code class="language-plaintext highlighter-rouge">AsyncImage</code> also lets you specify other parameters and options, and it might make the intermediate <code class="language-plaintext highlighter-rouge">Backport.AsyncImage</code> struct a bit unwieldy to support them all. So, another approach you could take is to use a static function that <em>looks</em> like a type:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Backport</span> <span class="k">where</span> <span class="kt">Content</span> <span class="o">==</span> <span class="kt">Any</span> <span class="p">{</span>
    
    <span class="kd">@ViewBuilder</span> <span class="kd">static</span> <span class="kd">func</span> <span class="kt">AsyncImage</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="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">if</span> <span class="k">#available</span><span class="p">(</span><span class="n">iOS</span> <span class="mi">15</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span> <span class="p">{</span>
            <span class="kt">SwiftUI</span><span class="o">.</span><span class="kt">AsyncImage</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="kt">MyCustomAsyncImage</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

<span class="p">}</span>
</code></pre></div></div>

<p>This has the same <em>callsite</em> syntax as the nested type (ie, they look identical at the point-of-use), but it doesn’t have the intermediate struct that has to potentially worry about holding a bunch of different configuration values or variations.</p>

<p>Pick the approach that feels simplest and easiest to maintain to you.</p>

<blockquote>
  <p>Note: This trick of using a capitalized function to “fake” a type name is one I find pretty handy. It bends the naming convention rules a bit, but in my opinion, the goal of “clarity at the callsite” is one that overrides all others.</p>
</blockquote>

<h2 id="keypaths-and-property-wrappers">KeyPaths and Property Wrappers</h2>

<p>Unfortunately, I have not come up with a good way to backport things like specific properties on SwiftUI’s <code class="language-plaintext highlighter-rouge">EnvironmentValues</code>, such as <code class="language-plaintext highlighter-rouge">.headerProminence</code>. In theory it should be similar to backporting methods, however every time I’ve gotten into attempting to implement this, I’ve run up against walls. The main problem I’ve found is one of <em>types</em>; for pre-iOS 15 devices, I’d need the type of the property to be <code class="language-plaintext highlighter-rouge">Backport.Prominence</code>, and in the other case I’d want it to be <code class="language-plaintext highlighter-rouge">SwiftUI.Prominence</code>. I’ve yet to find a satisfactory way of making this work.</p>

<p>Of course, if I ever come up with a way, I’ll make another post.</p>

<h2 id="upgrading">Upgrading</h2>

<p>Eventually, your app will drop support for iOS 14 and these shims will no longer be necessary. At this point, the easiest thing to do is to delete the no-longer-necessary types in your <code class="language-plaintext highlighter-rouge">Backport</code> extensions and rebuild your app. You’ll get errors that (for example) <code class="language-plaintext highlighter-rouge">Value of type 'Backport&lt;Text&gt;' has no member 'badge'</code>. This will reveal all the points in your code where you were using the backported <code class="language-plaintext highlighter-rouge">.badge</code> method. Go through these one by one, delete the intermediate <code class="language-plaintext highlighter-rouge">.backport</code> part of the call, and everything should compile again.</p>

<p>Alternatively, you could remove the <code class="language-plaintext highlighter-rouge">if #available(…)</code> check from the backport implementation and leave all those calls in place, but you would end up with an extraneous level of indirection in a bunch of callsites.</p>

<hr />

<p>Even though I’ve not found a way to use this in every scenario, I have found this to be useful in <em>most</em> scenarios. Using <code class="language-plaintext highlighter-rouge">Backport</code> like this has some really nice advantages:</p>

<ul>
  <li>if I keep my backport implementations organized, I have a one-stop shop in my codebase for where all my compatibility shims exist. This makes keeping track of them a bit easier.</li>
  <li>identifying usage throughout my codebase is also pretty easy. I search in my project for <code class="language-plaintext highlighter-rouge">backport.</code> and that finds methods <em>and</em> types.</li>
  <li>since <code class="language-plaintext highlighter-rouge">Backport&lt;Content&gt;</code> isn’t a SwiftUI <code class="language-plaintext highlighter-rouge">View</code>, it doesn’t interfere with SwiftUI’s underlying graph engine. In fact, SwiftUI never sees any intermediate view (unless I explicitly make one, such as one of the <code class="language-plaintext highlighter-rouge">Backport.AsyncImage</code> options).</li>
  <li>since <code class="language-plaintext highlighter-rouge">Backport&lt;Content&gt;</code> has no limits on its <code class="language-plaintext highlighter-rouge">Content</code>, this can be used for backporting just about <em>any</em> kind of new functionality from a system framework.</li>
  <li>by naming the methods and properties on <code class="language-plaintext highlighter-rouge">Backport</code> the same as their framework counterparts, searching through the codebase for how a system API gets used still points to the “right” points in my code</li>
  <li>using the same names also makes migrating off of this trivial: I delete the <code class="language-plaintext highlighter-rouge">backport.</code> characters and it Just Works™.</li>
</ul>

<p>How do you do compatibility shims and availability checking in your apps? Have you found something that works well for you? What challenges do you face adopting new APIs each year?</p>

<hr />

<p>This originally appeared as a few posts on <a href="https://twitter.com/davedelong">my twitter account</a>:</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">A handy Swift convenience thing I came up with recently (1/n):<br /><br />struct Backport&lt;Content&gt; {<br /> let content: Content<br />}<br /><br />extension View {<br /> var backport: Backport&lt;Self&gt; { Backport(content: self) }<br />}</p>&mdash; Dave DeLong (@davedelong) <a href="https://twitter.com/davedelong/status/1446151822800945155?ref_src=twsrc%5Etfw">October 7, 2021</a></blockquote>

<p>Also as a blog post from <a href="https://twitter.com/RalfEbert">Ralf Ebert</a>, which he wrote with my blessing after learning about this technique from me:</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">There is an even better way to use iOS-15-only View modifiers in older iOS versions: <a href="https://www.ralfebert.de/swiftui/backporting-ios-view-modifiers/">https://www.ralfebert.de/swiftui/backporting-ios-view-modifiers/</a><br /><br />Thanks <a href="https://twitter.com/davedelong">@davedelong</a> <a href="https://twitter.com/davedelong/status/1446151822800945155">https://twitter.com/davedelong/status/1446151822800945155</a> <a href="https://t.co/ELsFjWuuGM">pic.twitter.com/ELsFjWuuGM</a></p>&mdash; Ralf Ebert (@RalfEbert) <a href="https://twitter.com/RalfEbert/status/1446158282364686347">October 7, 2021</a></blockquote>

<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>]]></content><author><name></name></author><category term="swift" /><category term="tips" /><summary type="html"><![CDATA[Every year as new OS and Swift versions are released, the question comes up over and over again: “how do I use this new thing while also supporting older versions?”. While we have a bunch of “availability” tools at our disposal (and I’ll be using them in this post), they always come across as somewhat cumbersome: we need to do inline checks, or we have conditional logic flow that obfuscates the intent of some of our code, and so on.]]></summary></entry><entry><title type="html">Core Data and SwiftUI</title><link href="https://davedelong.com/blog/2021/04/03/core-data-and-swiftui/" rel="alternate" type="text/html" title="Core Data and SwiftUI" /><published>2021-04-03T00:00:00+00:00</published><updated>2021-04-03T00:00:00+00:00</updated><id>https://davedelong.com/blog/2021/04/03/core-data-and-swiftui</id><content type="html" xml:base="https://davedelong.com/blog/2021/04/03/core-data-and-swiftui/"><![CDATA[<p>In the previous post, I shared how you can create custom property wrappers that will work with SwiftUI’s view updating mechanism. I wrote that because I’ve got one other neat property wrapper to share, but understanding how it works requires knowing how to make custom wrappers. Now that I’ve got that out of the way…</p>

<h2 id="core-data">Core Data</h2>

<p>I really like Core Data. It’s an incredibly full-featured object persistence layer with an enormous number of really cool features. I know that it tends to get a bad rap amongst developers, but as I outlined in <a href="/blog/2018/05/09/the-laws-of-core-data/">my post on the “Laws” of Core Data</a>, I believe that most of the negative press comes from mis-using the framework.</p>

<p>I’m not saying that Core Data is without flaw; far from it. There are definitely changes I’d love to see in the API (a few of which I touch on in that post). But I do not believe it deserves the derision I so often hear from developers.</p>

<p>So when it comes to storing offline data in an app, my natural inclination is to use Core Data for the persistence layer. It’s pretty straight-forward to describe a schema in the editor and use the code generation features of Xcode to get it up and running fairly quickly.</p>

<p>Where I hesitate is introducing Core Data into the view layer. Despite the existence of things like <code class="language-plaintext highlighter-rouge">@FetchRequest</code>, I believe that it’s good to keep the layers of my personal apps separate. Core Data, dealing so closely with data organization and persistence, belongs at one of the <em>lowest</em> levels of my apps, and not in the UI.</p>

<p>This is a large part of why I suggest creating an abstraction layer for Core Data. An abstraction layer allows me to hide the nitty gritty details of data mutation and persistence from the parts of the app that deal with displaying that data in the UI.</p>

<p>An initial approach might suggest that creating a Core Data abstraction layer requires <a href="https://en.wikipedia.org/wiki/Don%27t_throw_the_baby_out_with_the_bathwater">foregoing all the nice affordances we have</a> for quickly and expressively extracting information from the persistent store. Not so! The setup we’ll be going over allows me to have the abstraction layer while still using cool things (like property wrappers) for retrieving data.</p>

<h2 id="the-roadmap">The Roadmap</h2>

<p>When designing a new API, I often like to start with the final syntax of how I want to use a thing, and then work backwards to make that syntax possible. As I was imagining a data abstraction layer, I came up with this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ContactList</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@Query</span><span class="p">(</span><span class="o">.</span><span class="n">all</span><span class="p">)</span> <span class="k">var</span> <span class="nv">contacts</span><span class="p">:</span> <span class="kt">QueryResults</span><span class="o">&lt;</span><span class="kt">Contact</span><span class="o">&gt;</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">List</span><span class="p">(</span><span class="n">contacts</span><span class="p">)</span> <span class="p">{</span> <span class="n">contact</span> <span class="k">in</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">contact</span><span class="o">.</span><span class="n">givenName</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is of course heavily inspired by <code class="language-plaintext highlighter-rouge">@FetchRequest</code>, but that’s not a bad thing. It’s pretty minimal: I define a query to fetch everything (<code class="language-plaintext highlighter-rouge">.all</code> the contacts), and I’ve got a basic <code class="language-plaintext highlighter-rouge">body</code> implementation. Like with <code class="language-plaintext highlighter-rouge">@FetchRequest</code>, I do need a special “results” type, which we’ll get to later.</p>

<p>We’re going to end up with a few different things to make this possible, which I’ll outline here:</p>

<ul>
  <li>a <code class="language-plaintext highlighter-rouge">DataStore</code> class</li>
  <li>a <code class="language-plaintext highlighter-rouge">Queryable</code> protocol</li>
  <li>a <code class="language-plaintext highlighter-rouge">QueryFilter</code> protocol</li>
  <li>a <code class="language-plaintext highlighter-rouge">Query</code> property wrapper</li>
  <li>a <code class="language-plaintext highlighter-rouge">QueryResults</code> struct</li>
</ul>

<h2 id="the-datastore">The DataStore</h2>

<p>First up is the DataStore. In my post on Core Data, I pointed out that it’s not necessary to create a “stack” object, since an <code class="language-plaintext highlighter-rouge">NSManagedObjectContext</code> has everything you need to get and manipulate the underlying objects. So, it might seem odd that the very first thing we’re going to do is wrap up the Core Data stack.</p>

<p>If you think about it though, it makes sense in this situation. We <em>want</em> to provide an abstraction layer on top of Core Data. The entire point is to <em>hide</em> Core Data from the rest of the app, and that implies hiding the persistence controllers from the rest of the app as well (the context, the store coordinator, etc). Thus, I create a <code class="language-plaintext highlighter-rouge">DataStore</code> object that encapsulates this. It manages the details of loading the persistent store and, as necessary, modifying the objects therein.</p>

<p>All mutations to the objects happen via the DataStore. I’ve come to really like the “one-way data flow” principle, where data always flows in a single direction. SwiftUI itself largely operates on this principle: as the state changes, the new UI description “flows” out of the new state. When it comes to my model layer, I do the same: the model objects flow out of the DataStore, and if something needs to change, I send a command to the DataStore instructing it to make the change, and new/modified values flow out of it.</p>

<h2 id="the-protocols">The Protocols</h2>

<p>The next two bits are closely related, so I’ll lump them together: The <code class="language-plaintext highlighter-rouge">Queryable</code> and <code class="language-plaintext highlighter-rouge">QueryFilter</code> protocols. They look like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">Queryable</span> <span class="p">{</span>
    <span class="kd">associatedtype</span> <span class="kt">Filter</span><span class="p">:</span> <span class="kt">QueryFilter</span>
    <span class="nf">init</span><span class="p">(</span><span class="nv">result</span><span class="p">:</span> <span class="kt">Filter</span><span class="o">.</span><span class="kt">ResultType</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">QueryFilter</span><span class="p">:</span> <span class="kt">Equatable</span> <span class="p">{</span>
    <span class="kd">associatedtype</span> <span class="kt">ResultType</span><span class="p">:</span> <span class="kt">NSFetchRequestResult</span>
    <span class="kd">func</span> <span class="nf">fetchRequest</span><span class="p">(</span><span class="n">_</span> <span class="nv">dataStore</span><span class="p">:</span> <span class="kt">DataStore</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">NSFetchRequest</span><span class="o">&lt;</span><span class="kt">ResultType</span><span class="o">&gt;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Queryable</code> protocol is the main type we’ll use for defining the in-memory <code class="language-plaintext highlighter-rouge">struct</code> values that we’ll be pulling out of the data store. It has an associated <code class="language-plaintext highlighter-rouge">Filter</code> type, which is the type that describes which values to fetch.</p>

<p>It also has an initializer, which takes the actual <code class="language-plaintext highlighter-rouge">NSManagedObject</code> instance and uses it to populate its properties. This must unavoidably be part of the interface; I’ve not found a good way to hide this yet.</p>

<p>The <code class="language-plaintext highlighter-rouge">QueryFilter</code> protocol defines the associated result type (typically <code class="language-plaintext highlighter-rouge">NSManagedObject</code> or one of your app-specific subclasses), as well as a function for turning the filter itself into an <code class="language-plaintext highlighter-rouge">NSFetchRequest</code>.</p>

<p>You’ll notice that I pass the <code class="language-plaintext highlighter-rouge">DataStore</code> in to the fetch request method. In my experience, the <code class="language-plaintext highlighter-rouge">DataStore</code> tends to end up holding cached information that a particular type might find useful when generating the fetch request, and being able to have that information on-hand during predicate creation has proven to be useful.</p>

<p>Adopting this protocol generally is as you’d expect:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Contact</span><span class="p">:</span> <span class="kt">Queryable</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">typealias</span> <span class="kt">Filter</span> <span class="o">=</span> <span class="kt">ContactFilter</span>

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

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">result</span><span class="p">:</span> <span class="kt">MyCoreDataContactObject</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">givenName</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="err">…</span>
        <span class="k">self</span><span class="o">.</span><span class="n">familyName</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="err">…</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">ContactFilter</span><span class="p">:</span> <span class="kt">QueryFilter</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">all</span> <span class="o">=</span> <span class="kt">ContactFilter</span><span class="p">()</span>
 
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">fetchRequest</span><span class="p">(</span><span class="n">_</span> <span class="nv">dataStore</span><span class="p">:</span> <span class="kt">DataStore</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">NSFetchRequest</span><span class="o">&lt;</span><span class="kt">MyCoreDataContactObject</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">f</span> <span class="o">=</span> <span class="kt">MyCoreDataContactObject</span><span class="o">.</span><span class="nf">fetchRequest</span><span class="p">()</span> <span class="k">as</span> <span class="kt">NSFetchRequest</span><span class="o">&lt;</span><span class="kt">MyCoreDataContactObject</span><span class="o">&gt;</span>
        <span class="n">f</span><span class="o">.</span><span class="n">predicate</span> <span class="o">=</span> <span class="kt">NSPredicate</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
        <span class="n">f</span><span class="o">.</span><span class="n">sortDescriptors</span> <span class="o">=</span> <span class="p">[</span><span class="kt">NSSortDescriptor</span><span class="p">(</span><span class="nv">key</span><span class="p">:</span> <span class="s">"givenName"</span><span class="p">,</span> <span class="nv">ascending</span><span class="p">:</span> <span class="kc">true</span><span class="p">)]</span>
        <span class="k">return</span> <span class="n">f</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this, we’ve got a simple immutable <code class="language-plaintext highlighter-rouge">Contact</code> struct that defines a filter with a single value (<code class="language-plaintext highlighter-rouge">.all</code>). The fetch request as a predicate to fetch everything, and it sorts the results in a particular order.</p>

<p>You can imagine having more bells and whistles on a particular <code class="language-plaintext highlighter-rouge">QueryFilter</code> type that are unique to the attributes of that type. You will need to implement the <code class="language-plaintext highlighter-rouge">NSPredicate</code> generation of course, but you’d have to write those predicates at <em>some</em> layer anyway.</p>

<h2 id="querying">Querying</h2>

<p>So, let’s start implementing the basics of <code class="language-plaintext highlighter-rouge">@Query</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@propertyWrapper</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Query</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Queryable</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">DynamicProperty</span> <span class="p">{</span>
    <span class="kd">@Environment</span><span class="p">(\</span><span class="o">.</span><span class="n">dataStore</span><span class="p">)</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">dataStore</span><span class="p">:</span> <span class="kt">DataStore</span>
    <span class="kd">@StateObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">core</span> <span class="o">=</span> <span class="kt">Core</span><span class="p">()</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">baseFilter</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="kt">Filter</span>

    <span class="kd">public</span> <span class="k">var</span> <span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">QueryResults</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span> <span class="p">{</span> <span class="n">core</span><span class="o">.</span><span class="n">results</span><span class="err"> </span><span class="p">}</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">filter</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">baseFilter</span> <span class="o">=</span> <span class="n">filter</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">update</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">core</span><span class="o">.</span><span class="nf">executeQuery</span><span class="p">(</span><span class="nv">dataStore</span><span class="p">:</span> <span class="n">dataStore</span><span class="p">,</span> <span class="nv">filter</span><span class="p">:</span> <span class="n">baseFilter</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>OK, so far this looks pretty normal. We’ve got a custom property wrapper that will trigger a view update pass in SwiftUI when either the <code class="language-plaintext highlighter-rouge">dataStore</code> in the Environment changes, or this “Core” object publishes a “will change” notification. Let’s make a simple first pass at this <code class="language-plaintext highlighter-rouge">Core</code> class.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Query</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Queryable</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">DynamicProperty</span> <span class="p">{</span>
    <span class="err">…</span>

    <span class="kd">private</span> <span class="kd">class</span> <span class="kt">Core</span><span class="p">:</span> <span class="kt">ObservableObject</span> <span class="p">{</span>
        <span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">results</span><span class="p">:</span> <span class="kt">QueryResults</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kt">QueryResults</span><span class="p">()</span>

        <span class="kd">func</span> <span class="nf">executeQuery</span><span class="p">(</span><span class="nv">dataStore</span><span class="p">:</span> <span class="kt">DataStore</span><span class="p">,</span> <span class="nv">filter</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="kt">Filter</span><span class="p">)</span> <span class="p">{</span>

            <span class="k">let</span> <span class="nv">fetchRequest</span> <span class="o">=</span> <span class="n">filter</span><span class="o">.</span><span class="nf">fetchRequest</span><span class="p">(</span><span class="n">dataStore</span><span class="p">)</span>
            <span class="k">let</span> <span class="nv">context</span> <span class="o">=</span> <span class="n">dataStore</span><span class="o">.</span><span class="n">viewContext</span>

            <span class="c1">// you MUST leave this as an NSArray</span>
            <span class="k">let</span> <span class="nv">results</span><span class="p">:</span> <span class="kt">NSArray</span> <span class="o">=</span> <span class="p">(</span><span class="k">try</span><span class="p">?</span> <span class="n">context</span><span class="o">.</span><span class="nf">fetch</span><span class="p">(</span><span class="n">fetchRequest</span><span class="p">))</span> <span class="p">??</span> <span class="kt">NSArray</span><span class="p">()</span>
            <span class="k">self</span><span class="o">.</span><span class="n">results</span> <span class="o">=</span> <span class="kt">QueryResults</span><span class="p">(</span><span class="nv">results</span><span class="p">:</span> <span class="n">results</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

<span class="p">}</span>
</code></pre></div></div>

<p>This is a <em>really</em> bare-bones implementation. So far, there’s nothing in here that couldn’t be done in the <code class="language-plaintext highlighter-rouge">update()</code> method of the property wrapper directly, and we’re always unconditionally executing the fetch request. We’re also not taking advantage of being an <code class="language-plaintext highlighter-rouge">ObservableObject</code> and broadcasting changes.</p>

<p>So for round 2, we’ll adjust this. For readability, I’ll leave off the outer layer of indentation.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">class</span> <span class="kt">Core</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">ObservableObject</span><span class="p">,</span> <span class="kt">NSFetchedResultsControllerDelegate</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">results</span> <span class="o">=</span> <span class="kt">QueryResults</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span><span class="p">()</span>

    <span class="k">var</span> <span class="nv">dataStore</span><span class="p">:</span> <span class="kt">DataStore</span><span class="p">?</span> 
    <span class="k">var</span> <span class="nv">filter</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="kt">Filter</span><span class="p">?</span>

    <span class="kd">private</span> <span class="k">var</span> <span class="nv">frc</span><span class="p">:</span> <span class="kt">NSFetchedResultsController</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">.</span><span class="kt">Filter</span><span class="o">.</span><span class="kt">ResultType</span><span class="o">&gt;</span><span class="p">?</span>

    <span class="kd">func</span> <span class="nf">fetchIfNecessary</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">ds</span> <span class="o">=</span> <span class="n">dataStore</span> <span class="k">else</span> <span class="p">{</span> 
            <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Attempting to execute a @Query but the DataStore is not in the environment"</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">f</span> <span class="o">=</span> <span class="n">filter</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Attempting to execute a @Query without a filter"</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="k">var</span> <span class="nv">shouldFetch</span> <span class="o">=</span> <span class="kc">false</span>

        <span class="k">let</span> <span class="nv">request</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="nf">fetchRequest</span><span class="p">(</span><span class="n">ds</span><span class="p">)</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">controller</span> <span class="o">=</span> <span class="n">frc</span> <span class="p">{</span>
            <span class="k">if</span> <span class="n">controller</span><span class="o">.</span><span class="n">fetchRequest</span><span class="o">.</span><span class="n">predicate</span> <span class="o">!=</span> <span class="n">request</span><span class="o">.</span><span class="n">predicate</span> <span class="p">{</span>
                <span class="n">controller</span><span class="o">.</span><span class="n">fetchRequest</span><span class="o">.</span><span class="n">predicate</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">predicate</span>
                <span class="n">shouldFetch</span> <span class="o">=</span> <span class="kc">true</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="n">controller</span><span class="o">.</span><span class="n">fetchRequest</span><span class="o">.</span><span class="n">sortDescriptors</span> <span class="o">!=</span> <span class="n">request</span><span class="o">.</span><span class="n">sortDescriptors</span> <span class="p">{</span>
                <span class="n">controller</span><span class="o">.</span><span class="n">fetchRequest</span><span class="o">.</span><span class="n">sortDescriptors</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">sortDescriptors</span>
                <span class="n">shouldFetch</span> <span class="o">=</span> <span class="kc">true</span>
            <span class="p">}</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">controller</span> <span class="o">=</span> <span class="kt">NSFetchedResultsController</span><span class="p">(</span><span class="nv">fetchRequest</span><span class="p">:</span> <span class="n">request</span><span class="p">,</span>
                                                        <span class="nv">managedObjectContext</span><span class="p">:</span> <span class="n">ds</span><span class="o">.</span><span class="n">viewContext</span><span class="p">,</span>
                                                        <span class="nv">sectionNameKeyPath</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">cacheName</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
            <span class="n">controller</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span>
            <span class="n">frc</span> <span class="o">=</span> <span class="n">controller</span>
            <span class="n">shouldFetch</span> <span class="o">=</span> <span class="kc">true</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="n">shouldFetch</span> <span class="p">{</span>
            <span class="k">try</span><span class="p">?</span> <span class="n">frc</span><span class="p">?</span><span class="o">.</span><span class="nf">performFetch</span><span class="p">()</span>
            <span class="k">let</span> <span class="nv">resultsArray</span> <span class="o">=</span> <span class="p">(</span><span class="n">frc</span><span class="p">?</span><span class="o">.</span><span class="n">fetchedObjects</span> <span class="k">as</span> <span class="kt">NSArray</span><span class="p">?)</span> <span class="p">??</span> <span class="kt">NSArray</span><span class="p">()</span>
            <span class="n">results</span> <span class="o">=</span> <span class="kt">QueryResults</span><span class="p">(</span><span class="nv">results</span><span class="p">:</span> <span class="n">resultsArray</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">controllerWillChangeContent</span><span class="p">(</span><span class="n">_</span> <span class="nv">controller</span><span class="p">:</span> <span class="kt">NSFetchedResultsController</span><span class="o">&lt;</span><span class="kt">NSFetchRequestResult</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">objectWillChange</span><span class="o">.</span><span class="nf">send</span><span class="p">()</span>
    <span class="p">}</span>
        
    <span class="kd">func</span> <span class="nf">controllerDidChangeContent</span><span class="p">(</span><span class="n">_</span> <span class="nv">controller</span><span class="p">:</span> <span class="kt">NSFetchedResultsController</span><span class="o">&lt;</span><span class="kt">NSFetchRequestResult</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">resultsArray</span> <span class="o">=</span> <span class="p">(</span><span class="n">controller</span><span class="o">.</span><span class="n">fetchedObjects</span> <span class="k">as</span> <span class="kt">NSArray</span><span class="p">?)</span> <span class="p">??</span> <span class="kt">NSArray</span><span class="p">()</span>
        <span class="n">results</span> <span class="o">=</span> <span class="kt">QueryResults</span><span class="p">(</span><span class="nv">results</span><span class="p">:</span> <span class="n">resultsArray</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is looking better. The <code class="language-plaintext highlighter-rouge">Core</code> class is now an <code class="language-plaintext highlighter-rouge">NSObject</code> subclass, so it can be the delegate of an <code class="language-plaintext highlighter-rouge">NSFetchedResultsController</code>; this allows us to get notifications from Core Data itself when the underlying data set changes. Or in other words, if we have the results of a <code class="language-plaintext highlighter-rouge">@Query</code> shown on screen and the underlying data store mutates the data, the <code class="language-plaintext highlighter-rouge">Core</code> object will get a notification, broadcast the <code class="language-plaintext highlighter-rouge">objectWillChange</code> signal, and the on-screen view will update to show the new or updated data. We’re checking in the <code class="language-plaintext highlighter-rouge">fetchIfNecessary()</code> method to make sure we don’t need to re-fetch data if things haven’t changed.</p>

<p>We’ll need to update the <code class="language-plaintext highlighter-rouge">Query</code> wrapper a bit as well:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@propertyWrapper</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Query</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Queryable</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">DynamicProperty</span> <span class="p">{</span>
    <span class="kd">@Environment</span><span class="p">(\</span><span class="o">.</span><span class="n">dataStore</span><span class="p">)</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">dataStore</span><span class="p">:</span> <span class="kt">DataStore</span>
    <span class="kd">@StateObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">core</span> <span class="o">=</span> <span class="kt">Core</span><span class="p">()</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">baseFilter</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="kt">Filter</span>

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

    <span class="kd">public</span> <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">update</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">core</span><span class="o">.</span><span class="n">dataStore</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span> <span class="n">core</span><span class="o">.</span><span class="n">dataStore</span> <span class="o">=</span> <span class="n">dataStore</span> <span class="p">}</span>
        <span class="k">if</span> <span class="n">core</span><span class="o">.</span><span class="n">filter</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span> <span class="n">core</span><span class="o">.</span><span class="n">filter</span> <span class="o">=</span> <span class="n">baseFilter</span> <span class="p">}</span>
        <span class="n">core</span><span class="o">.</span><span class="nf">fetchIfNecessary</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Before getting to the last bit, let’s step back and take a look at what’s going on:</p>

<ul>
  <li>The <code class="language-plaintext highlighter-rouge">Query</code> wrapper retrieves the data store from the environment, so it knows where to look for data</li>
  <li>The <code class="language-plaintext highlighter-rouge">Query</code> wrapper is created with a filter that describes what should be fetched from the data store</li>
  <li>Internally, there’s a <code class="language-plaintext highlighter-rouge">Core</code> class that uses the data store and filter to create an <code class="language-plaintext highlighter-rouge">NSFetchedResultsController</code>; this controller provides the results from the underlying Core Data store, as well as notifies of <em>changes</em> to the data, so our views can properly update. (That update is happening because the <code class="language-plaintext highlighter-rouge">Core</code> object calls its <code class="language-plaintext highlighter-rouge">objectWillChange.send()</code> method; SwiftUI is seeing this <code class="language-plaintext highlighter-rouge">ObservableObject</code> held in a <code class="language-plaintext highlighter-rouge">@StateObject</code> and is tracking it for changes.</li>
  <li>The <code class="language-plaintext highlighter-rouge">Query</code> wrapper provides the results back out to the view via this <code class="language-plaintext highlighter-rouge">QueryResults</code> type.</li>
</ul>

<p>The last things to notice before we move on are:</p>

<ol>
  <li>We’re explicitly keeping the results from the controller as an <code class="language-plaintext highlighter-rouge">NSArray</code>, not an <code class="language-plaintext highlighter-rouge">Array&lt;T.Filter.ResultType&gt;</code></li>
  <li>This array holds <code class="language-plaintext highlighter-rouge">NSManagedObject</code> instances, not values of type <code class="language-plaintext highlighter-rouge">T</code></li>
  <li>The filter is currently read-only… what if we want to change it dynamically? (Imagine showing a list of results that we want to change the sort order, or start typing to match text, etc)</li>
</ol>

<h2 id="queryresults">QueryResults</h2>

<p>In order to make our data work nicely with things like <code class="language-plaintext highlighter-rouge">List(…)</code> and <code class="language-plaintext highlighter-rouge">ForEach(…)</code>, it needs to conform to the <code class="language-plaintext highlighter-rouge">RandomAccessCollection</code> protocol. So we need to take the array of results we got from Core Data and wrap it up. Fortunately, this only requires a few lines of code:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">QueryResults</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Queryable</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">RandomAccessCollection</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">results</span><span class="p">:</span> <span class="kt">NSArray</span>
    
    <span class="kd">internal</span> <span class="nf">init</span><span class="p">(</span><span class="nv">results</span><span class="p">:</span> <span class="kt">NSArray</span> <span class="o">=</span> <span class="kt">NSArray</span><span class="p">())</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">results</span> <span class="o">=</span> <span class="n">results</span>
    <span class="p">}</span>
    
    <span class="kd">public</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="n">results</span><span class="o">.</span><span class="n">count</span> <span class="p">}</span>
    <span class="kd">public</span> <span class="k">var</span> <span class="nv">startIndex</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">}</span>
    <span class="kd">public</span> <span class="k">var</span> <span class="nv">endIndex</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span> <span class="n">count</span> <span class="p">}</span>
    
    <span class="kd">public</span> <span class="nf">subscript</span><span class="p">(</span><span class="nv">position</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">T</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">object</span> <span class="o">=</span> <span class="n">results</span><span class="o">.</span><span class="nf">object</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">position</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">T</span><span class="o">.</span><span class="kt">Filter</span><span class="o">.</span><span class="kt">ResultType</span>
        <span class="k">return</span> <span class="kt">T</span><span class="p">(</span><span class="nv">result</span><span class="p">:</span> <span class="n">object</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is also where using an <code class="language-plaintext highlighter-rouge">NSArray</code> (instead of a Swift <code class="language-plaintext highlighter-rouge">Array</code>) is important. When fetching data from Core Data, we don’t always know how many values we’ll be getting back. There could be 5 or 5 million. Core Data solves this problem by using a <a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html"><em>subclass</em> of <code class="language-plaintext highlighter-rouge">NSArray</code></a> that will dynamically pull in data from the underlying store on demand.</p>

<p>On the other hand, a Swift <code class="language-plaintext highlighter-rouge">Array</code> requires having every element in the array all at once, and bridging an <code class="language-plaintext highlighter-rouge">NSArray</code> to a Swift <code class="language-plaintext highlighter-rouge">Array</code> requires retrieving <em>every single value</em>. So if you execute a query that returns 5 million values and then turn the results into an <code class="language-plaintext highlighter-rouge">Array&lt;NSManagedObject&gt;</code>, your app will grind to a halt while it fetches 5 million objects in to memory.</p>

<p>We avoid this by avoiding the <code class="language-plaintext highlighter-rouge">NSArray</code>-to-<code class="language-plaintext highlighter-rouge">Array</code> conversion. This is fine for our cases, because the Core Data array is smart enough to act as a “random access collection”, so we can easily turn around and ask for the value at the 3 millionth position.</p>

<p>Once we’ve retrieved the value, we run it through the initializer defined on the <code class="language-plaintext highlighter-rouge">Queryable</code> protocol, and we end up with the value to show publicly in the UI.</p>

<blockquote>
  <p>Food for thought: This <code class="language-plaintext highlighter-rouge">QueryResults</code> type re-builds the <code class="language-plaintext highlighter-rouge">T</code> value every time. How would you add caching so that if it’s built the value once, it doesn’t need to build it again?</p>
</blockquote>

<h2 id="mutating-the-filter">Mutating the Filter</h2>

<p>It’d be nice to have a way to modify the filter from the UI, so we can control things like sort order or change aspects of the predicate. For example, if you’re showing a list of contacts, it’d be nice to have a search field so you can search for contacts that match some user-provided text. Let’s imagine that the <code class="language-plaintext highlighter-rouge">ContactsFilter</code> type has a <code class="language-plaintext highlighter-rouge">var searchString = ""</code> property that gets translated into a predicate that searches for that text in the underlying data store.</p>

<p>As before, let’s invent the syntax we want, and then write the code to make it happen:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ContactList</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@Query</span><span class="p">(</span><span class="o">.</span><span class="n">all</span><span class="p">)</span> <span class="k">var</span> <span class="nv">contacts</span><span class="p">:</span> <span class="kt">QueryResults</span><span class="o">&lt;</span><span class="kt">Contact</span><span class="o">&gt;</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">TextField</span><span class="p">(</span><span class="s">"Search"</span><span class="p">,</span> <span class="nv">text</span><span class="p">:</span> <span class="n">$contacts</span><span class="o">.</span><span class="n">searchString</span><span class="p">)</span>
        <span class="kt">List</span><span class="p">(</span><span class="n">contacts</span><span class="p">)</span> <span class="p">{</span> <span class="n">contact</span> <span class="k">in</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">contact</span><span class="o">.</span><span class="n">givenName</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That looks pretty neat! If we can pull out a mutable filter (<code class="language-plaintext highlighter-rouge">Binding&lt;T.Filter&gt;</code>), we can use the existing mechanism on <code class="language-plaintext highlighter-rouge">Binding</code> to scope it down to a single field (<code class="language-plaintext highlighter-rouge">@dynamicMemberLookup</code>) and bind the filter <em>directly</em> into a <code class="language-plaintext highlighter-rouge">TextField</code>. Let’s make this happen.</p>

<p>The <code class="language-plaintext highlighter-rouge">$contacts</code> syntax is some nifty compiler magic to access the <code class="language-plaintext highlighter-rouge">projectedValue</code> on the property wrapper (if it has one). We want to implement this on <code class="language-plaintext highlighter-rouge">Query</code> and provide a <code class="language-plaintext highlighter-rouge">Binding&lt;T.Filter&gt;</code>. Getting the value should return either the current modified filter, or the initial filter. Setting the value should change it on the <code class="language-plaintext highlighter-rouge">Core</code> object:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Query</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Queryable</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">DynamicProperty</span> <span class="p">{</span>
    <span class="kd">@Environment</span><span class="p">(\</span><span class="o">.</span><span class="n">dataStore</span><span class="p">)</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">dataStore</span><span class="p">:</span> <span class="kt">DataStore</span>
    <span class="kd">@StateObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">core</span> <span class="o">=</span> <span class="kt">Core</span><span class="p">()</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">baseFilter</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="kt">Filter</span>

    <span class="kd">public</span> <span class="k">var</span> <span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">QueryResults</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span> <span class="p">{</span> <span class="n">core</span><span class="o">.</span><span class="n">results</span><span class="err"> </span><span class="p">}</span>

    <span class="kd">public</span> <span class="k">var</span> <span class="nv">projectedValue</span><span class="p">:</span> <span class="kt">Binding</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">.</span><span class="kt">Filter</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">Binding</span><span class="p">(</span><span class="nv">get</span><span class="p">:</span> <span class="p">{</span> <span class="n">core</span><span class="o">.</span><span class="n">filter</span> <span class="p">??</span> <span class="n">baseFilter</span> <span class="p">},</span>
                       <span class="nv">set</span><span class="p">:</span> <span class="p">{</span>
                        <span class="k">if</span> <span class="n">core</span><span class="o">.</span><span class="n">filter</span> <span class="o">!=</span> <span class="nv">$0</span> <span class="p">{</span> 
                            <span class="n">core</span><span class="o">.</span><span class="n">objectWillChange</span><span class="o">.</span><span class="nf">send</span><span class="p">()</span> 
                            <span class="n">core</span><span class="o">.</span><span class="n">filter</span> <span class="o">=</span> <span class="nv">$0</span>  
                        <span class="p">}</span>
                       <span class="p">})</span>
    <span class="p">}</span>

    <span class="err">…</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There we go! We return a <code class="language-plaintext highlighter-rouge">Binding</code> (which is really a wrapper around <code class="language-plaintext highlighter-rouge">getter</code> and <code class="language-plaintext highlighter-rouge">setter</code> closures) that can get the value by using the <code class="language-plaintext highlighter-rouge">core</code>’s filter, and falling back to the <code class="language-plaintext highlighter-rouge">baseFilter</code> if the Core doesn’t have one. Setting the value will check to make sure its a <em>different</em> value (so we don’t inadvertently trigger unnecessary UI updates if nothing is changing), then will broadcast an imminent change to SwiftUI, and set the new value into the <code class="language-plaintext highlighter-rouge">Core</code> object.</p>

<p>Broadcasting the change means the containing view will be re-built, the <code class="language-plaintext highlighter-rouge">Core</code> will notice the changed filter, and the <code class="language-plaintext highlighter-rouge">NSFetchedResultsController</code> will be updated and re-executed to provide new results!</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>At the surface, this seems like it’s doing a decent amount of work with little discernible gain. All this, just so I can have a <code class="language-plaintext highlighter-rouge">struct</code> value? Well yes… but actually no. As you develop the model layer in your apps, your <code class="language-plaintext highlighter-rouge">Filter</code> objects will inevitably become more complex, and having a single place where you build your fetch requests will be invaluable. It makes mutating the underlying managed object model much simpler, because you only have one or two places where you need to change code. You come to appreciate how the compiler stops you from inadvertently mutating values, and since <code class="language-plaintext highlighter-rouge">Queryable</code> values typically are structs, you can avoid a decent amount of the queue restriction rules that plague novice Core Data adopters.</p>

<p>In my own usage of this pattern, I’ve found it to be extremely powerful. One neat thing that may not be immediately obvious is that you can have multiple <code class="language-plaintext highlighter-rouge">Queryable</code> types that are backed by the <em>same kind</em> of underlying <code class="language-plaintext highlighter-rouge">NSManagedObject</code>. So if you have a particularly complex managed object and only need certain parts of it in certain situations, you can construct different <code class="language-plaintext highlighter-rouge">Queryable</code> representations for those situations. You can also do things like make your filter conform to <code class="language-plaintext highlighter-rouge">Codable</code> to easily make persistable filters.</p>

<p>I’ve also come to really appreciate how this <em>guarantees</em> that the values used to populate my UI are read-only: they’re immutable structs! I couldn’t mutate them if I wanted to! This also allows me to build in-memory values that can be <em>supplemented</em> with additional information. For example, if I update the <code class="language-plaintext highlighter-rouge">Queryable</code> initializer to take the <code class="language-plaintext highlighter-rouge">DataStore</code> as well as the backing managed object, I have the opportunity to fill out rich value types in ways that Core Data can’t do itself.</p>

<p>Overall, the boilerplate I have to put in to make types be <code class="language-plaintext highlighter-rouge">Queryable</code> seems like a good tradeoff for the features, safety, and expressivity I get in return.</p>

<hr />

<p>So, the big question… Should you use this code?</p>

<p><strong>NO</strong> you should not use this code.</p>

<p>There are two main reasons you should hesitate to lift this code into your project:</p>

<ol>
  <li>
    <p>I wrote all of this in a text editor. None of it has been checked for completeness. I’ve pulled portions of it from my personal implementation, but I’m sure I’ve missed some edge cases that you’ll want to figure out.</p>
  </li>
  <li>
    <p>Before you take all of this and splat it into your code base, you need to evaluate what problems you’re trying to solve. I came up with this code in an attempt to follow my personal Laws of Core Data, and the design of this code is a reflection of those constraints. Your code may well be working with a different set of a constraints, and so it is imperative that you figure out what is best for your code. Maybe it’s this exactly. Maybe it’s something similar. But maybe it’s completely different. That’s fine.</p>
  </li>
</ol>]]></content><author><name></name></author><category term="swift" /><category term="code" /><category term="swiftui" /><summary type="html"><![CDATA[In the previous post, I shared how you can create custom property wrappers that will work with SwiftUI’s view updating mechanism. I wrote that because I’ve got one other neat property wrapper to share, but understanding how it works requires knowing how to make custom wrappers. Now that I’ve got that out of the way…]]></summary></entry><entry><title type="html">Custom Property Wrappers for SwiftUI</title><link href="https://davedelong.com/blog/2021/04/02/custom-property-wrappers-for-swiftui/" rel="alternate" type="text/html" title="Custom Property Wrappers for SwiftUI" /><published>2021-04-02T00:00:00+00:00</published><updated>2021-04-02T00:00:00+00:00</updated><id>https://davedelong.com/blog/2021/04/02/custom-property-wrappers-for-swiftui</id><content type="html" xml:base="https://davedelong.com/blog/2021/04/02/custom-property-wrappers-for-swiftui/"><![CDATA[<p>As I’ve been working with SwiftUI, I’ve come up with some custom property wrappers that I want to share with you. Most of these are property wrappers that can be easily accomplished using other approaches, but I like these for their <a href="https://www.youtube.com/watch?v=bsPR0LqetYU">expressivity</a>.</p>

<h2 id="making-custom-property-wrappers">Making Custom Property Wrappers</h2>

<p>The easiest way to make a custom property wrapper that triggers SwiftUI view updates is to simply wrap another property wrapper:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@propertyWrapper</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">ReinventedState</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">DynamicProperty</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="k">var</span> <span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">T</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There’s one crucial piece to make this work, and that’s to declare that your property wrapper conforms to <code class="language-plaintext highlighter-rouge">DynamicProperty</code>. If you do this, then the SwiftUI runtime will discover your property wrapper instance and start tracking it for changes. Since your property wrapper is using other built-in property wrappers, SwiftUI will recognize that your wrapper is <em>dependent</em> on the others, and when those update, yours will too.</p>

<p>There is an <code class="language-plaintext highlighter-rouge">update()</code> method that’s part of the <code class="language-plaintext highlighter-rouge">DynamicProperty</code> protocol, but you typically don’t need to implement it (the default implementation does nothing) unless your wrapper is tracking some internal state that it needs to update before the <code class="language-plaintext highlighter-rouge">.wrappedValue</code> can be invoked in a <code class="language-plaintext highlighter-rouge">var body</code> somewhere. <code class="language-plaintext highlighter-rouge">update()</code> is called right before the view’s body is called.</p>

<p>But this is really all there is to it.</p>

<h2 id="feature">@Feature</h2>

<p>One of the most useful property wrappers I’ve made is a simple “feature flag” trigger. It’s fairly common to have feature flags to control whether app-global features are available. Some apps go so far as to download a set of feature flag values and use those to dynamically adjust behavior. I like it for doing this like enabling a hidden debug menu.</p>

<p>The idea behind <code class="language-plaintext highlighter-rouge">@Feature</code> is that I can use it to track a particular <em>key path</em> on a <code class="language-plaintext highlighter-rouge">Features</code> singleton (or environment object) and then when that feature flag changes, the <code class="language-plaintext highlighter-rouge">@Feature</code> wrapper triggers a change at the view level. It looks something like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="kt">Features</span><span class="p">:</span> <span class="kt">ObservableObject</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">shared</span> <span class="o">=</span> <span class="kt">Features</span><span class="p">()</span>
    <span class="kd">private</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>

    <span class="kd">@Published</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">isDebugMenuEnabled</span> <span class="o">=</span> <span class="kc">false</span>
<span class="p">}</span>

<span class="kd">@propertyWrapper</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Feature</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">DynamicProperty</span> <span class="p">{</span>
    <span class="kd">@ObservedObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">features</span><span class="p">:</span> <span class="kt">Features</span>

    <span class="kd">private</span> <span class="k">let</span> <span class="nv">keyPath</span><span class="p">:</span> <span class="kt">KeyPath</span><span class="o">&lt;</span><span class="kt">Features</span><span class="p">,</span> <span class="kt">T</span><span class="o">&gt;</span>
    
    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">keyPath</span><span class="p">:</span> <span class="kt">KeyPath</span><span class="o">&lt;</span><span class="kt">Features</span><span class="p">,</span> <span class="kt">T</span><span class="o">&gt;</span><span class="p">,</span> <span class="nv">features</span><span class="p">:</span> <span class="kt">Features</span> <span class="o">=</span> <span class="o">.</span><span class="n">shared</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">keyPath</span> <span class="o">=</span> <span class="n">keyPath</span>
        <span class="k">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</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">T</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">features</span><span class="p">[</span><span class="nv">keyPath</span><span class="p">:</span> <span class="n">keyPath</span><span class="p">]</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now in a view, I can use it like so:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">AppSettingsView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@Feature</span><span class="p">(\</span><span class="o">.</span><span class="n">isDebugMenuEnabled</span><span class="p">)</span> <span class="k">var</span> <span class="nv">showDebugMenu</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">Form</span> <span class="p">{</span>
            <span class="k">if</span> <span class="n">showDebugMenu</span> <span class="p">{</span>
                <span class="kt">NavigationLink</span><span class="p">(</span><span class="nv">destination</span><span class="p">:</span> <span class="kt">DebugSettings</span><span class="p">())</span> <span class="p">{</span> <span class="kt">Text</span><span class="p">(</span><span class="s">"Debug Settings"</span><span class="p">)</span> <span class="p">}</span>
            <span class="p">}</span>
            <span class="err">…</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now any time the <code class="language-plaintext highlighter-rouge">Features</code> object publishes a change from anywhere in the app, my <code class="language-plaintext highlighter-rouge">@Feature</code> wrapper will pick it up, and the <code class="language-plaintext highlighter-rouge">AppSettingsView</code> will be automatically re-built (if it’s on-screen).</p>

<p>A couple notes about this:</p>

<ul>
  <li>Yes, this is a singleton and singletons are “bad”. It’s illustrative here. You could just as easily use an <code class="language-plaintext highlighter-rouge">@EnvironmentObject</code> instead of an <code class="language-plaintext highlighter-rouge">@ObservedObject</code> in the implementation and you’d get the same result</li>
  <li>Since the <em>entire</em> <code class="language-plaintext highlighter-rouge">Features</code> object is being observed, then technically a change to any feature will trigger a change to every <code class="language-plaintext highlighter-rouge">@Feature</code> wrapper. You could imagine working around this by doing something clever with specializing on <code class="language-plaintext highlighter-rouge">T: Equatable</code> and checking for differences on a particular key path before triggering a change. If you did this, you wouldn’t be observing the <code class="language-plaintext highlighter-rouge">Features</code> type directly, but some sort of object that wraps it and re-publishes a particular value.</li>
  <li><code class="language-plaintext highlighter-rouge">@Feature</code> relies on a <code class="language-plaintext highlighter-rouge">KeyPath</code> to know what value to pull out. This guarantees some type-safety, but it also means I don’t end up with stringly-typed keys scattered around my app (more on this shortly).</li>
  <li>By using <code class="language-plaintext highlighter-rouge">@Feature</code> instead of observing the <code class="language-plaintext highlighter-rouge">Features</code> value directly with <code class="language-plaintext highlighter-rouge">@EnvironmentObject</code> or something, it’s easier to search through the a codebase to find all uses of a feature: I can search for <code class="language-plaintext highlighter-rouge">@Feature</code> or <code class="language-plaintext highlighter-rouge">.isDebugMenuEnabled</code> and be confident that I’ll have found everything.</li>
  <li>Strongly-typed key paths means that if I use the Refactor command to renamed a property, it gets changed automatically everywhere throughout the app.</li>
</ul>

<h2 id="appsetting-and-scenesetting">AppSetting and SceneSetting</h2>

<p><code class="language-plaintext highlighter-rouge">@AppSetting</code> and <code class="language-plaintext highlighter-rouge">@SceneSetting</code> are two that are nearly identical in construction to <code class="language-plaintext highlighter-rouge">@Feature</code>, but differ only in where they pull their values from. <code class="language-plaintext highlighter-rouge">@Feature</code> relies on a <code class="language-plaintext highlighter-rouge">Features</code> class, whereas <code class="language-plaintext highlighter-rouge">@AppSetting</code> and <code class="language-plaintext highlighter-rouge">@SceneSetting</code> would rely on <code class="language-plaintext highlighter-rouge">AppSettings</code> and <code class="language-plaintext highlighter-rouge">SceneSettings</code> instances, respectively.</p>

<p>I won’t go too much into the code here, beyond saying that I built these because I don’t like scattering the string keys for <code class="language-plaintext highlighter-rouge">UserDefaults</code> values everywhere, and I much prefer the type safety of using key paths.</p>

<p>So, I built an <code class="language-plaintext highlighter-rouge">AppSettings</code> class:</p>

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

    <span class="kd">@AppStorage</span><span class="p">(</span><span class="s">"appSetting1"</span><span class="p">)</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">appSetting1</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">42</span>
    <span class="kd">@AppStorage</span><span class="p">(</span><span class="s">"appSetting2"</span><span class="p">)</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">appSetting2</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"Hello"</span>
    <span class="err">…</span>

<span class="p">}</span>
</code></pre></div></div>

<p>and a <code class="language-plaintext highlighter-rouge">SceneSettings</code> class:</p>

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

    <span class="kd">@SceneStorage</span><span class="p">(</span><span class="s">"sceneValue1"</span><span class="p">)</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">sceneSetting1</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">54</span>
    <span class="kd">@SceneStorage</span><span class="p">(</span><span class="s">"sceneValue2"</span><span class="p">)</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">sceneSetting2</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"World"</span>
    <span class="err">…</span>

<span class="p">}</span>
</code></pre></div></div>

<p>Their respective property wrappers are basically identical to <code class="language-plaintext highlighter-rouge">@Feature</code>, but obviously use a key path rooted in the corresponding type, and then use an <code class="language-plaintext highlighter-rouge">@ObservedObject</code>/<code class="language-plaintext highlighter-rouge">@EnvironmentObject</code> of that type. They also implement the <code class="language-plaintext highlighter-rouge">projectedValue</code> property so that I can create bindings to use these values using the <code class="language-plaintext highlighter-rouge">$…</code> syntax.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">SomeDetailView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@SceneSetting</span><span class="p">(\</span><span class="o">.</span><span class="n">shouldShowThatOneInfoSection</span><span class="p">)</span> <span class="k">var</span> <span class="nv">showInfo</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">DisclosureGroup</span><span class="p">(</span><span class="s">"Detail Info"</span><span class="p">,</span> <span class="nv">isExpanded</span><span class="p">:</span> <span class="n">$showInfo</span><span class="p">)</span> <span class="p">{</span>
            <span class="kt">SomeInfoView</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="err">…</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Like with <code class="language-plaintext highlighter-rouge">@Feature</code>, these have the same benefits of type safety, refactorability, and avoiding stringly-typed keys.</p>

<h2 id="other-one-off-wrappers">Other One-Off Wrappers</h2>

<p>I’ll sometimes create one-off property wrappers to access specific things in the environment or specific global values. I do these mainly to draw attention to their usage in a particular view. For example, it can be easier to scan through a view and spot an <code class="language-plaintext highlighter-rouge">@OpenURL var openURL</code> value than it is to scan through a list of a handful of <code class="language-plaintext highlighter-rouge">@Environment(\.…)</code> values and notice the right key path.</p>

<p>When deciding what to use, I always try to err on the side of readability. The compiler doesn’t care what I type, as long as it can correctly generate the executable code. So when deciding what to do, I remember that more often than not, I’m writing this <em>for me</em>, not for the compiler. Therefore, I write it in a way that will (hopefully) make the most sense to me when I come back to this code in weeks, months, or years and have inevitably forgotten what it does.</p>

<h2 id="where-we-go-from-here">Where We Go From Here</h2>

<p>This is a quick overview of creating custom property wrappers that trigger SwiftUI view updates, and a couple of examples of some that I’ve found to be useful.</p>

<p>The thing I <em>really</em> want to share requires having a bit of background around creating custom property wrappers. I call it <code class="language-plaintext highlighter-rouge">@Query</code> and it is how I follow <a href="/blog/2018/05/09/the-laws-of-core-data/">my Laws of Core Data</a> in SwiftUI.</p>]]></content><author><name></name></author><category term="swift" /><category term="code" /><category term="swiftui" /><summary type="html"><![CDATA[As I’ve been working with SwiftUI, I’ve come up with some custom property wrappers that I want to share with you. Most of these are property wrappers that can be easily accomplished using other approaches, but I like these for their expressivity.]]></summary></entry><entry><title type="html">Exploiting String Interpolation For Fun And For Profit</title><link href="https://davedelong.com/blog/2021/03/04/exploiting-string-interpolation-for-fun-and-for-profit/" rel="alternate" type="text/html" title="Exploiting String Interpolation For Fun And For Profit" /><published>2021-03-04T00:00:00+00:00</published><updated>2021-03-04T00:00:00+00:00</updated><id>https://davedelong.com/blog/2021/03/04/exploiting-string-interpolation-for-fun-and-for-profit</id><content type="html" xml:base="https://davedelong.com/blog/2021/03/04/exploiting-string-interpolation-for-fun-and-for-profit/"><![CDATA[<p>A while ago I was playing around with Swift’s string interpolation functionality and come up with something cool I thought I’d share with you.</p>

<h2 id="whats-string-interpolation">What’s String Interpolation?</h2>

<p>In Swift, string interpolation is the functionality that lets us do substitutions into strings using the <code class="language-plaintext highlighter-rouge">\(...)</code> syntax:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">name</span> <span class="o">=</span> <span class="s">"world"</span>
<span class="k">let</span> <span class="nv">greeting</span> <span class="o">=</span> <span class="s">"Hello, </span><span class="se">\(</span><span class="n">name</span><span class="se">)</span><span class="s">!"</span>
</code></pre></div></div>

<p>This works because of a couple of underlying pieces provided by the standard library and the compiler:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">ExpressibleByStringInterpolation</code></li>
  <li>Some nifty syntactic transformations</li>
</ol>

<p>The <code class="language-plaintext highlighter-rouge">ExpressibleByStringInterpolation</code> follows the <a href="https://en.wikipedia.org/wiki/Builder_pattern">builder pattern</a>, which describes constructing a value by first creating an intermediate “builder”, tossing some configuration calls at it, and then asking it to “build” the final output value.</p>

<p>Thus, the definition of <code class="language-plaintext highlighter-rouge">ExpressibleByStringInterpolation</code> is straightforward:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ExpressibleByStringInterpolation</span><span class="p">:</span> <span class="kt">ExpressibleByStringLiteral</span> <span class="p">{</span>
    <span class="kd">associatedtype</span> <span class="kt">StringInterpolation</span><span class="p">:</span> <span class="kt">StringInterpolationProtocol</span>
    <span class="nf">init</span><span class="p">(</span><span class="nv">stringInterpolation</span><span class="p">:</span> <span class="k">Self</span><span class="o">.</span><span class="kt">StringInterpolation</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This protocol expresses three requirements for adopting types:</p>

<ol>
  <li>You need to provide a type called <code class="language-plaintext highlighter-rouge">StringInterpolation</code> that itself conforms to the <code class="language-plaintext highlighter-rouge">StringInterpolationProtocol</code> protocol. This is the “builder” used to construct stuff</li>
  <li>You need to have an initializer for your type that can take one of these builder instances</li>
  <li>You <em>also</em> need to conform to the <code class="language-plaintext highlighter-rouge">ExpressibleByStringLiteral</code> protocol</li>
</ol>

<p>The last requirement is a bit odd at first, but it makes sense. If you allow your types to be constructed like this…</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// build an instance of MyType using ExpressibleByStringInterpolation</span>
<span class="k">let</span> <span class="nv">myType</span><span class="p">:</span> <span class="kt">MyType</span> <span class="o">=</span> <span class="s">"Hello, </span><span class="se">\(</span><span class="n">name</span><span class="se">)</span><span class="s">!"</span>
</code></pre></div></div>

<p>…then presumably you should <em>also</em> allow your types to be constructed like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// build an instance of MyType using ExpressibleByStringLiteral</span>
<span class="k">let</span> <span class="nv">myType</span><span class="p">:</span> <span class="kt">MyType</span> <span class="o">=</span> <span class="s">"Hello, world!"</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">StringInterpolationProtocol</code> is where things start getting weird, because it can’t be fully expressed in Swift syntax. The declaration of the protocol is this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">StringInterpolationProtocol</span> <span class="p">{</span>
    <span class="kd">associatedtype</span> <span class="kt">StringLiteralType</span><span class="p">:</span> <span class="n">_ExpressibleByBuiltinStringLiteral</span>
    <span class="nf">init</span><span class="p">(</span><span class="nv">literalCapacity</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">interpolationCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">appendLiteral</span><span class="p">(</span><span class="n">_</span> <span class="nv">literal</span><span class="p">:</span> <span class="k">Self</span><span class="o">.</span><span class="kt">StringLiteralType</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>A <code class="language-plaintext highlighter-rouge">StringLiteralType</code> (basically, always use <code class="language-plaintext highlighter-rouge">String</code> for this unless you have really bizarre situation where that’s not right), an initializer (to construct the builder itself), and an <code class="language-plaintext highlighter-rouge">appendLiteral</code> method.</p>

<p>But where’s all the stuff about appending the interpolated values?</p>

<p>The answer is that they’re there, but Swift doesn’t have a way to describe a protocol like this. The gist is that you need a method <em>called</em> <code class="language-plaintext highlighter-rouge">appendInterpolation</code>, but there aren’t any requirements on what the <em>parameters</em> for this method are.</p>

<p>To understand this a bit better, let’s detour and take a look at the transformation that happens with you use string interpolation in code.</p>

<h2 id="syntactic-transformation">Syntactic Transformation</h2>

<p>When we write an interpolated string, the compiler transforms it to use the builder. If we write something like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">MyType</span> <span class="o">=</span> <span class="s">"Hello, </span><span class="se">\(</span><span class="n">name</span><span class="se">)</span><span class="s">!"</span>
</code></pre></div></div>

<p>Then at compile-time it gets turned into this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">builder</span> <span class="o">=</span> <span class="kt">MyType</span><span class="o">.</span><span class="kt">StringInterpolation</span><span class="p">(</span><span class="nv">literalCapacity</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span> <span class="nv">interpolationCount</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">builder</span><span class="o">.</span><span class="nf">appendLiteral</span><span class="p">(</span><span class="s">"Hello, "</span><span class="p">)</span>
<span class="n">builder</span><span class="o">.</span><span class="nf">appendInterpolation</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="n">builder</span><span class="o">.</span><span class="nf">appendLiteral</span><span class="p">(</span><span class="s">"!"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="kt">MyType</span><span class="p">(</span><span class="nv">stringInterpolation</span><span class="p">:</span> <span class="n">builder</span><span class="p">)</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">literalCapacity</code> parameter is the number of <code class="language-plaintext highlighter-rouge">Characters</code> present in the literal portion of the string, and the <code class="language-plaintext highlighter-rouge">interpolationCount</code> indicates how many substitutions there are.</p>

<p>What’s interesting here is that <code class="language-plaintext highlighter-rouge">appendInterpolation</code> call. Basically, anything that we put inside the <code class="language-plaintext highlighter-rouge">\(…)</code> part of the interpolation <em>becomes</em> the arguments to the <code class="language-plaintext highlighter-rouge">appendInterpolation</code> method. So <code class="language-plaintext highlighter-rouge">\(foo: bar)</code> becomes <code class="language-plaintext highlighter-rouge">….appendInterpolation(foo: bar)</code>, <code class="language-plaintext highlighter-rouge">\(age, formatter: someNumberFormatter)</code> becomes <code class="language-plaintext highlighter-rouge">….appendInterpolation(age, formatter: someNumberFormatter)</code>, and so on.</p>

<p>Thus, we can create string interpolators that limit their accepted substitutions based on what kinds of <code class="language-plaintext highlighter-rouge">appendInterpolation</code> methods we write. You could, for example, create an interpolator that only accepts interpolated integers by only providing an <code class="language-plaintext highlighter-rouge">appendInterpolation(_ int: Int)</code> method.</p>

<h2 id="swiftuis-localizedstringkey">SwiftUI’s LocalizedStringKey</h2>

<p>String interpolation is how SwiftUI is able to build up <code class="language-plaintext highlighter-rouge">NSLocalizedString</code> keys to look up translations in your <code class="language-plaintext highlighter-rouge">.strings</code> files. When we write <code class="language-plaintext highlighter-rouge">Text("Hello, \(name)!")</code> in SwiftUI, we’re not passing a <code class="language-plaintext highlighter-rouge">String</code> instance to the <code class="language-plaintext highlighter-rouge">Text</code> initializer; we’re passing a <code class="language-plaintext highlighter-rouge">LocalizedStringKey</code> instance, and that type happens to be <code class="language-plaintext highlighter-rouge">ExpressibleByStringInterpolation</code>.</p>

<p>And it’s also not a standard interpolation implementation like the one we find on <code class="language-plaintext highlighter-rouge">String</code>. In addition to building up the “fallback” string to use in the case where it can’t find a proper translation, it builds up the key itself. You can imagine how this might work:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">LocalizedStringKey</span><span class="o">.</span><span class="kt">StringInterpolation</span><span class="p">:</span> <span class="kt">StringInterpolationProtocol</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">key</span> <span class="o">=</span> <span class="s">""</span>
    <span class="k">var</span> <span class="nv">fallback</span> <span class="o">=</span> <span class="s">""</span>

    <span class="k">mutating</span> <span class="kd">func</span> <span class="nf">appendLiteral</span><span class="p">(</span><span class="n">_</span> <span class="nv">literal</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">key</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">literal</span><span class="p">)</span>
        <span class="n">fallback</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">literal</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">mutating</span> <span class="kd">func</span> <span class="n">appendInterpolation</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">value</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">key</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="s">"%@"</span><span class="p">)</span>
        <span class="n">fallback</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">value</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this, you can build up both the fallback value (<code class="language-plaintext highlighter-rouge">"Hello, world!"</code>) and the key to use to look up the translation in your strings file (<code class="language-plaintext highlighter-rouge">"Hello, %@!"</code>).</p>

<h2 id="a-solution-in-search-of-a-problem">A Solution in Search of a Problem</h2>

<p>The fact that interpolation is a <em>syntactic</em> transformation means we can get creative. Let’s take a closer look at the <code class="language-plaintext highlighter-rouge">appendInterpolation(…)</code> bit.</p>

<p>When you have a line of code like this, there are technically <em>three</em> different things that could be happening:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>foo.doSomething(bar)
</code></pre></div></div>

<p>The obvious first case is that you have a <code class="language-plaintext highlighter-rouge">func doSomething(_ bar: Bar)</code> method on the type in question.</p>

<p>The next slightly-less-obvious-but-still-kind-of-common case is when you have a <code class="language-plaintext highlighter-rouge">var doSomething: (Bar) -&gt; Void</code> property on the type. In this case, <code class="language-plaintext highlighter-rouge">.doSomething(bar)</code> is retrieving the closure and executing it, all on the same line.</p>

<p>The least-obvious case is that you’ve got a <code class="language-plaintext highlighter-rouge">var doSomething: OtherType</code> property, and that <code class="language-plaintext highlighter-rouge">OtherType</code> is <a href="https://nshipster.com/callable/#type-instance-member-static-dynamic"><code class="language-plaintext highlighter-rouge">@dynamicCallable</code></a>. <code class="language-plaintext highlighter-rouge">@dynamicCallable</code> is almost never used in Swift, because it’s kind of weird and was added to make it easier to bridge in libraries from other languages. It allows you to have a value and then directly “execute” that value by simply throwing <code class="language-plaintext highlighter-rouge">(…)</code> on the end.</p>

<p>We can exploit <code class="language-plaintext highlighter-rouge">@dynamicCallable</code> to get a <em>whole lot more information</em> from string interpolation.</p>

<h3 id="inventing-a-problem">Inventing a Problem</h3>

<p>Let’s imagine that we wanted to make it easy to declare some sort of “route” functionality for an app, and that we’d want to support dealing with incoming paths and automatically parse out certain things. For example, a path with <code class="language-plaintext highlighter-rouge">/person/1234</code> might result in a <code class="language-plaintext highlighter-rouge">["person": PersonID(1234)]</code> value. Or a <code class="language-plaintext highlighter-rouge">/person/1234/items/42/delete</code> might result in: <code class="language-plaintext highlighter-rouge">["person": PersonID(1234), "item": ItemID(42), "action": ItemAction.delete]</code>.</p>

<p>We can imagine how we might express this with string interpolation:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">route</span><span class="p">:</span> <span class="kt">Route</span> <span class="o">=</span> <span class="s">"/person/</span><span class="se">\(</span><span class="nv">person</span><span class="p">:</span> <span class="kt">PersonID</span><span class="o">.</span><span class="k">self</span><span class="se">)</span><span class="s">/items/</span><span class="se">\(</span><span class="nv">item</span><span class="p">:</span> <span class="kt">ItemID</span><span class="o">.</span><span class="k">self</span><span class="se">)</span><span class="s">/</span><span class="se">\(</span><span class="nv">action</span><span class="p">:</span> <span class="kt">ItemAction</span><span class="o">.</span><span class="k">self</span><span class="se">)</span><span class="s">"</span>
</code></pre></div></div>

<p>With <code class="language-plaintext highlighter-rouge">@dynamicCallable</code> and string interpolation, we can build this.</p>

<p>It starts by having a <code class="language-plaintext highlighter-rouge">Route</code> type that is <code class="language-plaintext highlighter-rouge">ExpressibleByStringInterpolation</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Route</span><span class="p">:</span> <span class="kt">ExpressibleByStringInterpolation</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">StringInterpolation</span> <span class="o">=</span> <span class="kt">RouteMatcher</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="nv">stringInterpolation</span><span class="p">:</span> <span class="kt">RouteMatcher</span><span class="p">)</span> <span class="p">{</span> <span class="err">…</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Our <code class="language-plaintext highlighter-rouge">RouteMatcher</code> will build up a <a href="https://en.wikipedia.org/wiki/Regular_expression">regular expression</a> to do the parsing.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// yes, this is a class. That will be explained shortly.</span>
<span class="kd">class</span> <span class="kt">RouteMatcher</span><span class="p">:</span> <span class="kt">StringInterpolationProtocol</span> <span class="p">{</span>
    <span class="kd">internal</span> <span class="k">var</span> <span class="nv">pattern</span> <span class="o">=</span> <span class="s">""</span>
    <span class="kd">required</span> <span class="nf">init</span><span class="p">(</span><span class="nv">literalCapacity</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">interpolationCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">isSpecialRegexCharacter</span><span class="p">(</span><span class="n">_</span> <span class="nv">char</span><span class="p">:</span> <span class="kt">Character</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="err">… </span><span class="p">}</span>

    <span class="kd">func</span> <span class="nf">appendLiteral</span><span class="p">(</span><span class="n">_</span> <span class="nv">literal</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">character</span> <span class="k">in</span> <span class="n">literal</span> <span class="p">{</span>
            <span class="k">if</span> <span class="nf">isSpecialRegexCharacter</span><span class="p">(</span><span class="n">character</span><span class="p">)</span> <span class="p">{</span> <span class="n">pattern</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="s">"</span><span class="se">\\</span><span class="s">"</span><span class="p">)</span> <span class="p">}</span>
            <span class="n">pattern</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">character</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So far, so good. But now things are going to get hairy. We need to say allow an interpolation segment (<code class="language-plaintext highlighter-rouge">\(person: Person.self)</code>) where we have a <em>named</em> parameter and accept a <em>type</em> as the value. We can’t just have a whole bunch of <code class="language-plaintext highlighter-rouge">appendInterpolation(…)</code> methods on our <code class="language-plaintext highlighter-rouge">RouteMatcher</code>, because we don’t know what name the user will want to type in. This is where <code class="language-plaintext highlighter-rouge">@dynamicCallable</code> comes in.</p>

<p>So, we define a <em>property</em> called <code class="language-plaintext highlighter-rouge">appendInterpolation</code> that returns our <code class="language-plaintext highlighter-rouge">@dynamicCallable</code> type:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">RouteMatcher</span><span class="p">:</span> <span class="kt">StringInterpolationProtocol</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">appendInterpolation</span><span class="p">:</span> <span class="kt">Capture</span> <span class="p">{</span> <span class="kt">Capture</span><span class="p">(</span><span class="nv">matcher</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">@dynamicCallable</span>
<span class="kd">struct</span> <span class="kt">Capture</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">matcher</span><span class="p">:</span> <span class="kt">RouteMatcher</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Next, we implement the <code class="language-plaintext highlighter-rouge">dynamicallyCall()</code> method that defines the “keyword arguments” syntax. This will allow us to execute the <code class="language-plaintext highlighter-rouge">Capture</code> type using <em>named</em> argument parameters:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@dynamicCallable</span>
<span class="kd">struct</span> <span class="kt">Capture</span> <span class="p">{</span>
    <span class="c1">// since RouteMatcher is a reference type, modifying it here is modifying the "right" value</span>
    <span class="k">let</span> <span class="nv">matcher</span><span class="p">:</span> <span class="kt">RouteMatcher</span>

    <span class="kd">func</span> <span class="nf">dynamicallyCall</span><span class="p">(</span><span class="n">withKeywordArguments</span> <span class="nv">args</span><span class="p">:</span> <span class="kt">KeyValuePairs</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">Any</span><span class="o">.</span><span class="k">Type</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// args is a collection of (String, Any.Type) tuples</span>
		<span class="c1">// TODO: relay the information back to the RouteMatcher</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="c1">// example:</span>
<span class="c1">// let c: Capture = …</span>
<span class="c1">// c(foo: bar)</span>
</code></pre></div></div>

<p>At this point, our matcher is <em>almost</em> complete. If you build this code, you’ll find that Xcode complains about missing protocol requirements:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error: type conforming to 'StringInterpolationProtocol' does not implement a valid 'appendInterpolation' method
</code></pre></div></div>

<p>The compiler is still looking for an actual <em>method</em>, even though we’re not going to actually be using it. Still, we need to make the compiler happy, so we add this to our <code class="language-plaintext highlighter-rouge">RouteMatcher</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">RouteMatcher</span><span class="p">:</span> <span class="kt">StringInterpolationProtocol</span> <span class="p">{</span>
    <span class="err">…</span>
    <span class="kd">@_disfavoredOverload</span>
    <span class="kd">func</span> <span class="nf">appendInterpolation</span><span class="p">(</span><span class="n">_</span> <span class="nv">willThisExecute</span><span class="p">:</span> <span class="kt">Never</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
    <span class="err">…</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This satisfies the compiler (it sees a <em>method</em> called “appendInterpolation”), but there are two things that make sure it never actually gets used:</p>

<ol>
  <li>
    <p>The <code class="language-plaintext highlighter-rouge">@_disfavoredOverload</code> annotation tells the compiler that “if you have to choose between this thing and another thing, prefer the other thing”. Yes, the underscore technically means it’s “private” and therefore “use it at your own risk”. But, it works and is used <em>all over</em> in SwiftUI. 🤷‍♂️</p>
  </li>
  <li>
    <p>The use of <code class="language-plaintext highlighter-rouge">Never</code> as the parameter type means that even if the compiler messes up and picks this method, it won’t work because (<em>gasp</em>) you can’t ever create an instance of <code class="language-plaintext highlighter-rouge">Never</code> to pass to the method.</p>
  </li>
</ol>

<h3 id="solving-the-imaginary-problem">Solving the Imaginary Problem</h3>

<p>With this, the compiler now happily rewrites the string interpolation code, and our implementation means that we can capture named arguments and the type values provided to them. If you were to follow this through, you might end up with code that looks approximately like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">PathExtractible</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">?(</span><span class="nv">pathValue</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">Route</span><span class="p">:</span> <span class="kt">ExpressibleByStringInterpolation</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">matcher</span><span class="p">:</span> <span class="p">(</span><span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Dictionary</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">Any</span><span class="o">&gt;</span><span class="p">?</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="n">stringLiteral</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="k">let</span> <span class="nv">m</span> <span class="o">=</span> <span class="kt">Matcher</span><span class="p">(</span><span class="nv">literalCapacity</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">interpolationCount</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
        <span class="n">m</span><span class="o">.</span><span class="nf">appendLiteral</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
        <span class="k">self</span><span class="o">.</span><span class="n">matcher</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="nf">build</span><span class="p">()</span>
    <span class="p">}</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="nv">stringInterpolation</span><span class="p">:</span> <span class="kt">Matcher</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">matcher</span> <span class="o">=</span> <span class="n">stringInterpolation</span><span class="o">.</span><span class="nf">build</span><span class="p">()</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">match</span><span class="p">(</span><span class="n">_</span> <span class="nv">path</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Dictionary</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">Any</span><span class="o">&gt;</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nf">matcher</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="kt">Matcher</span><span class="p">:</span> <span class="kt">StringInterpolationProtocol</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">escaped</span> <span class="o">=</span> <span class="kt">CharacterSet</span><span class="p">(</span><span class="nv">charactersIn</span><span class="p">:</span> <span class="err">#</span><span class="s">"[</span><span class="err">\</span><span class="s">^$.|?*+()"</span><span class="err">#</span><span class="p">)</span>
    <span class="kd">fileprivate</span> <span class="k">var</span> <span class="nv">pattern</span> <span class="o">=</span> <span class="kt">Array</span><span class="o">&lt;</span><span class="kt">Unicode</span><span class="o">.</span><span class="kt">Scalar</span><span class="o">&gt;</span><span class="p">()</span>
    <span class="kd">fileprivate</span> <span class="k">var</span> <span class="nv">extractions</span> <span class="o">=</span> <span class="kt">Array</span><span class="o">&lt;</span><span class="p">(</span><span class="kt">String</span><span class="p">,</span> <span class="kt">PathExtractible</span><span class="o">.</span><span class="k">Type</span><span class="p">)</span><span class="o">&gt;</span><span class="p">()</span>
    
    <span class="k">var</span> <span class="nv">appendInterpolation</span><span class="p">:</span> <span class="kt">Capture</span> <span class="p">{</span> <span class="kt">Capture</span><span class="p">(</span><span class="nv">matcher</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="p">}</span>

    <span class="kd">public</span> <span class="kd">required</span> <span class="nf">init</span><span class="p">(</span><span class="nv">literalCapacity</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">interpolationCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">appendLiteral</span><span class="p">(</span><span class="n">_</span> <span class="nv">literal</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">char</span> <span class="k">in</span> <span class="n">literal</span><span class="o">.</span><span class="n">unicodeScalars</span> <span class="p">{</span>
            <span class="k">if</span> <span class="n">escaped</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">char</span><span class="p">)</span> <span class="p">{</span> <span class="n">pattern</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="s">"</span><span class="se">\\</span><span class="s">"</span><span class="p">)</span> <span class="p">}</span>
            <span class="n">pattern</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="kd">@_disfavoredOverload</span>
    <span class="kd">func</span> <span class="nf">appendInterpolation</span><span class="p">(</span><span class="n">_</span> <span class="nv">willThisExecute</span><span class="p">:</span> <span class="kt">Never</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">build</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="o">-&gt;</span> <span class="kt">Dictionary</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">Any</span><span class="o">&gt;</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">f</span> <span class="o">=</span> <span class="s">"^"</span> <span class="o">+</span> <span class="kt">String</span><span class="p">(</span><span class="kt">String</span><span class="o">.</span><span class="kt">UnicodeScalarView</span><span class="p">(</span><span class="n">pattern</span><span class="p">))</span> <span class="o">+</span> <span class="s">"$"</span>
        <span class="k">let</span> <span class="nv">regex</span> <span class="o">=</span> <span class="k">try!</span> <span class="kt">NSRegularExpression</span><span class="p">(</span><span class="nv">pattern</span><span class="p">:</span> <span class="n">f</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="p">[])</span>
        <span class="k">let</span> <span class="nv">extractions</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">extractions</span>
        
        <span class="k">return</span> <span class="p">{</span> <span class="n">path</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">match</span> <span class="o">=</span> <span class="n">regex</span><span class="o">.</span><span class="nf">firstMatch</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="n">path</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="p">[],</span> <span class="nv">range</span><span class="p">:</span> <span class="kt">NSRange</span><span class="p">(</span><span class="nv">location</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">length</span><span class="p">:</span> <span class="n">path</span><span class="o">.</span><span class="n">utf16</span><span class="o">.</span><span class="n">count</span><span class="p">))</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">return</span> <span class="kc">nil</span>
            <span class="p">}</span>
            <span class="k">guard</span> <span class="n">match</span><span class="o">.</span><span class="n">numberOfRanges</span> <span class="o">==</span> <span class="n">extractions</span><span class="o">.</span><span class="n">count</span> <span class="o">+</span> <span class="mi">1</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">return</span> <span class="kc">nil</span>
            <span class="p">}</span>
            
            <span class="k">var</span> <span class="nv">extracted</span> <span class="o">=</span> <span class="kt">Dictionary</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">Any</span><span class="o">&gt;</span><span class="p">()</span>

            <span class="c1">// capture groups are 1-indexed</span>
            <span class="k">for</span> <span class="n">captureGroup</span> <span class="k">in</span> <span class="mi">1</span> <span class="o">..&lt;</span> <span class="n">match</span><span class="o">.</span><span class="n">numberOfRanges</span> <span class="p">{</span>
                <span class="k">let</span> <span class="nv">substring</span> <span class="o">=</span> <span class="p">(</span><span class="n">path</span> <span class="k">as</span> <span class="kt">NSString</span><span class="p">)</span><span class="o">.</span><span class="nf">substring</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">match</span><span class="o">.</span><span class="nf">range</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">captureGroup</span><span class="p">))</span>
                <span class="k">let</span> <span class="p">(</span><span class="nv">name</span><span class="p">,</span> <span class="nv">type</span><span class="p">)</span> <span class="o">=</span> <span class="n">extractions</span><span class="p">[</span><span class="n">captureGroup</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
                <span class="k">guard</span> <span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="n">type</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">pathValue</span><span class="p">:</span> <span class="n">substring</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="k">return</span> <span class="kc">nil</span>
                <span class="p">}</span>
                <span class="n">extracted</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
            <span class="p">}</span>
            <span class="k">return</span> <span class="n">extracted</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="kd">@dynamicCallable</span>
    <span class="kd">struct</span> <span class="kt">Capture</span> <span class="p">{</span>
        <span class="kd">fileprivate</span> <span class="k">let</span> <span class="nv">matcher</span><span class="p">:</span> <span class="kt">Matcher</span>
        
        <span class="kd">func</span> <span class="nf">dynamicallyCall</span><span class="p">(</span><span class="n">withKeywordArguments</span> <span class="nv">args</span><span class="p">:</span> <span class="kt">KeyValuePairs</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">PathExtractible</span><span class="o">.</span><span class="k">Type</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">for</span> <span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">type</span><span class="p">)</span> <span class="k">in</span> <span class="n">args</span> <span class="p">{</span>
                <span class="n">matcher</span><span class="o">.</span><span class="n">pattern</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="nv">contentsOf</span><span class="p">:</span> <span class="s">"(.+?)"</span><span class="o">.</span><span class="n">unicodeScalars</span><span class="p">)</span>
                <span class="n">matcher</span><span class="o">.</span><span class="n">extractions</span><span class="o">.</span><span class="nf">append</span><span class="p">((</span><span class="n">name</span><span class="p">,</span> <span class="n">type</span><span class="p">))</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
<span class="p">}</span>
</code></pre></div></div>

<p>This uses a “PathExtractible” protocol to know how to construct values extracted from the path. You could do a bit of convenience work to automatically provide an implementation for things that can already be created from strings, like <code class="language-plaintext highlighter-rouge">RawRepresentable</code> or <code class="language-plaintext highlighter-rouge">LosslessStringConvertible</code> types.</p>

<p>Using this code would look something like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">route</span><span class="p">:</span> <span class="kt">Route</span> <span class="o">=</span> <span class="s">"/api/profile/</span><span class="se">\(</span><span class="nv">memberID</span><span class="p">:</span> <span class="kt">Int</span><span class="o">.</span><span class="k">self</span><span class="se">)</span><span class="s">/lists/</span><span class="se">\(</span><span class="nv">listID</span><span class="p">:</span> <span class="kt">String</span><span class="o">.</span><span class="k">self</span><span class="se">)</span><span class="s">"</span>
<span class="k">let</span> <span class="nv">validArguments</span> <span class="o">=</span> <span class="n">route</span><span class="o">.</span><span class="nf">match</span><span class="p">(</span><span class="s">"/api/profile/1234/lists/5678"</span><span class="p">)</span> 
<span class="c1">// validArguments = ["memberID": 1234, "listID": "5678"]</span>

<span class="k">let</span> <span class="nv">invalidArguments</span> <span class="o">=</span> <span class="n">route</span><span class="o">.</span><span class="nf">match</span><span class="p">(</span><span class="s">"/api/feed"</span><span class="p">)</span>
<span class="c1">// invalidArguments == nil</span>
</code></pre></div></div>

<p>There are a couple of downsides to this approach, which is why I’ve yet to come up with a situation where this code would actually be useful:</p>

<ol>
  <li>There’s no way (that I’ve found) to encode the generic types being capture in the <code class="language-plaintext highlighter-rouge">Route</code> itself. This means that every path that comes in has to be naïvely tried on every possible <code class="language-plaintext highlighter-rouge">Route</code> in order to find a match</li>
  <li>The lack of specifiable generics also means that the matched values comes out as an (unfortunate) <code class="language-plaintext highlighter-rouge">Dictionary&lt;String: Any&gt;</code>. So even though you <em>know</em> that the types are the right kind, you’ll still have to do some force-casting in order to get them in a useful form. Perhaps there’s something more that could be done here using a custom <code class="language-plaintext highlighter-rouge">Decoder</code> type.</li>
</ol>

<hr />

<p>I share this with you because string interpolation is neat, the <code class="language-plaintext highlighter-rouge">@dynamicCallable</code> stuff is cool, and hopefully you’ll come up with something that uses this that we all can benefit from.</p>]]></content><author><name></name></author><category term="swift" /><summary type="html"><![CDATA[A while ago I was playing around with Swift’s string interpolation functionality and come up with something cool I thought I’d share with you.]]></summary></entry><entry><title type="html">HTTP in Swift, Part 17: Brain Dump</title><link href="https://davedelong.com/blog/2020/10/03/http-in-swift-part-17-brain-dump/" rel="alternate" type="text/html" title="HTTP in Swift, Part 17: Brain Dump" /><published>2020-10-03T00:00:00+00:00</published><updated>2020-10-03T00:00:00+00:00</updated><id>https://davedelong.com/blog/2020/10/03/http-in-swift-part-17-brain-dump</id><content type="html" xml:base="https://davedelong.com/blog/2020/10/03/http-in-swift-part-17-brain-dump/"><![CDATA[<p>I was planning on having a few more posts on different loaders you can build using this architecture, but for the sake of “finishing” this series up, I’ve decided to forego a post-per-loader and instead highlight the main points of a few of them.</p>

<h2 id="openid">OpenID</h2>

<p>We’ve already take a look at how to implement a loader that authorizes requests via an OAuth 2 flow, but there’s an abstraction that exists on top of that, called <a href="https://en.wikipedia.org/wiki/OpenID">OpenID</a>. With the OAuth loader, we needed to specify things like the login url, the url for refreshing tokens, and so on. OpenID allows for identity providers to abstract that away by shipping down a manifest that contains all of these urls (and other idiosyncrasies of the protocol).</p>

<p>If we wanted to implement OpenID ourselves, we’d need a preliminary state in our state machine to first fetch this manifest, and then use it as the basis for subsequent state logic. Alternatively, we could wrap an existing implementation of OpenID (<a href="https://github.com/openid/AppAuth-iOS">such as the official implementation</a>) in a custom <code class="language-plaintext highlighter-rouge">HTTPLoader</code> subclass, and allow that library to perform the complex logic. In this case, our <code class="language-plaintext highlighter-rouge">HTTPLoader</code> subclass would serve as an <a href="https://en.wikipedia.org/wiki/Adapter_pattern">adapter</a> between the API provided by the library, and the API wanted by the HTTP loader chain.</p>

<h2 id="caching">Caching</h2>

<p>Conceptually, a caching loader should be relatively straight-forward to understand. When a request enters this loader, it examines the request (and perhaps an <code class="language-plaintext highlighter-rouge">HTTPRequestOption</code> to indicate if caching is allowed) and sees if it matches any responses that have been persisted (in memory, on disk, etc). If such a response exists, then the loader returns <em>that</em> response instead of sending the request further down the chain.</p>

<p>If a response doesn’t exist, it continues with typical request execution, but also inserts another completion handler so that it can capture the response and (if the right conditions are met), persist it to use for future requests.</p>

<h2 id="deduplication">Deduplication</h2>

<p>Deduplication is similar to caching, in that when a request comes in, the loader sees if it’s similar to an already in-progress request. If it is, then the new request is set aside, and when the original request gets a response, that response is duplicated to the second request.</p>

<h2 id="redirection">Redirection</h2>

<p>There are a couple of ways to handle redirected requests.</p>

<p>By default, <code class="language-plaintext highlighter-rouge">URLSession</code> will follow redirects, unless you specifically override the <code class="language-plaintext highlighter-rouge">willPerformHTTPRedirection</code> delegate method on <code class="language-plaintext highlighter-rouge">URLSessionTaskDelegate</code>. So, you could do that and then conditionally allow redirection on requests based on a particular <code class="language-plaintext highlighter-rouge">HTTPRequestOption</code> you’ve created.</p>

<p>Alternatively, you could unconditionally deny redirections at the <code class="language-plaintext highlighter-rouge">URLSession</code> level, and then have a separate <code class="language-plaintext highlighter-rouge">RedirectionFollowingLoader</code> that takes incoming requests, <em>duplicates</em> them, and sends the duplicates down the chain. When the duplicate comes back, the loader examines the response and sees if its a redirection response. If it is, then it constructs a new request for the redirect, and sends that back down.</p>

<p>Once the loader gets back a non-redirection response, it uses that response as the response for the original request and sends it back out. You would need some logic to detect redirection loops and break out of them, but the key idea here is to send down a <em>copy</em> of a request, so that you get a chance to examine the response before deciding what to do about it.</p>

<h2 id="certificate-pinning">Certificate Pinning</h2>

<p>In principle, certificate pinning should look like any other HTTPLoader: a request comes in, and before it gets sent to the next one, the certificate for the target server is validated against a certificate attached to the request as an <code class="language-plaintext highlighter-rouge">HTTPRequestOption</code>.</p>

<p>In practice, this is a little bit more difficult, because certificates are only available as the connection to the remote server is being negotiated down in the <code class="language-plaintext highlighter-rouge">URLSessionLoader</code>. Because of this, the course of action here is to not have a separate <code class="language-plaintext highlighter-rouge">CertificatePinningLoader</code>, but instead to provide a <code class="language-plaintext highlighter-rouge">CertificateValidator</code> value to an <code class="language-plaintext highlighter-rouge">HTTPRequest</code> that <em>can be used</em> if a loader needs to do some certificate validation (similar to <a href="https://github.com/Alamofire/Alamofire/blob/master/Source/ServerTrustEvaluation.swift">Alamofire’s <code class="language-plaintext highlighter-rouge">ServerTrustEvaluating</code> protocol</a>).</p>

<p>Then our <code class="language-plaintext highlighter-rouge">URLSessionLoader</code> needs to be updated to use a delegate, and implement the delegate method to handle a <code class="language-plaintext highlighter-rouge">URLAuthenticationChallenge</code>, and then consult that option for the request when it receives the <code class="language-plaintext highlighter-rouge">.serverTrust</code> challenge.</p>

<h2 id="peer-to-peer">Peer-to-peer</h2>

<p>A peer-to-peer loader is interesting, because it stems from the realization that the contract for the <code class="language-plaintext highlighter-rouge">HTTPLoader</code> says nothing about <em>which device the response comes from</em>. We’ve already seen examples of loaders that will return fake responses (for mocking) or re-use responses (caching and de-duplication). A P2P loader is one that can decide to ship a request off to another device, and allow <em>that</em> device to provide a response.</p>

<p>This could be done via a myriad of technologies, ranging from something like <a href="https://developer.apple.com/documentation/multipeerconnectivity">MultipeerConnectivity</a> to <a href="https://developer.apple.com/documentation/corebluetooth">Bluetooth</a> or direct socket connections. The possibilities here are pretty vast.</p>

<p>The astute observer will also realize that the <code class="language-plaintext highlighter-rouge">URLSessionLoader</code> we created early on fits in this sort of category. That’s a loader that “off loads” the responsibility of producing a request to another device. It happens to be a device that is also an HTTP server, but our loading stack doesn’t directly have to know that.</p>

<h2 id="streaming-responses">Streaming Responses</h2>

<p>One area where this framework does not work terribly well is with streaming responses. This is pretty apparent: we’ve built everything around the expectation that a discrete and finite request has a discrete and finite response. Streaming kind of breaks that expectation. We <em>can</em> do a streamed body in an upload, because the process of sending that stream is part of sending our single discrete request.</p>

<p>There are some kinds of streamed bodies we could handle, such as file downloads. For these, we’d want to provide an <a href="https://developer.apple.com/documentation/foundation/outputstream"><code class="language-plaintext highlighter-rouge">OutputStream</code></a> (or similar) to say “put any bytes you get back here”; by default this could be a stream to an in-memory <code class="language-plaintext highlighter-rouge">Data</code> value. This would allow us stream a response directly to a file, instead of going through an in-memory <code class="language-plaintext highlighter-rouge">Data</code> value.</p>

<p>For a live video stream, we could provide an <code class="language-plaintext highlighter-rouge">OutputStream</code> that pipes the data into an <code class="language-plaintext highlighter-rouge">AVSession</code>. However, we’d be explicitly foregoing some of the semantics of “single request, single response” in order to make this work. We would also need to be very careful about how we implement request duplication (such as would be needed by a redirecting loader).</p>

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

<p>There is a lot we can do with this framework. A few things are somewhat complicated and require working around/with specific implementation details of system APIs (such as certificate pinning, streamed responses, etc). On the whole though, this approach of modeling networking as “send a request, and eventually get a response” allows us to build extremely flexible, composeable, and customizable networking stacks.</p>

<hr />

<p>In the next (and likely final) post, we’ll be zooming back out to look at the high-level overview of the framework we’ve created, and see how it fits in with other Swift technologies.</p>]]></content><author><name></name></author><category term="http" /><category term="swift" /><summary type="html"><![CDATA[I was planning on having a few more posts on different loaders you can build using this architecture, but for the sake of “finishing” this series up, I’ve decided to forego a post-per-loader and instead highlight the main points of a few of them.]]></summary></entry><entry><title type="html">HTTP in Swift, Part 18: Wrapping Up</title><link href="https://davedelong.com/blog/2020/10/03/http-in-swift-part-18-wrapping-up/" rel="alternate" type="text/html" title="HTTP in Swift, Part 18: Wrapping Up" /><published>2020-10-03T00:00:00+00:00</published><updated>2020-10-03T00:00:00+00:00</updated><id>https://davedelong.com/blog/2020/10/03/http-in-swift-part-18-wrapping-up</id><content type="html" xml:base="https://davedelong.com/blog/2020/10/03/http-in-swift-part-18-wrapping-up/"><![CDATA[<p>Over the course of this series, we’ve started with a simple idea and taken it to some pretty fascinating places. The idea we started with is that a network layer can be abstracted out to the idea of “I send this request, and eventually I get a response”.</p>

<p>I started working on this approach after reading Rob Napier’s <a href="https://robnapier.net/start-with-a-protocol">blog post on protocols</a> on protocols. In it, he makes the point that we seem to misunderstand the seminal “Protocol Oriented Programming” idea introduced by <strike>Dave Abrahams</strike> Crusty at WWDC 2015. We especially miss the point when it comes to networking, and Rob’s subsequent posts go in to this idea further.</p>

<p>One of the things I hope you’ve realized throughout this blog post series is that <em>nowhere in this series did I ever talk about <code class="language-plaintext highlighter-rouge">Codable</code></em>. Nothing in this series is generic (with the minor exception of making it easy to specify a request body). There is no mention of deserialization or JSON or decoding responses or anything. This is extremely deliberate.</p>

<p>The point of HTTP is simple: You send an HTTP request (which we saw has a very well-defined structure) and you get back an HTTP response (which has a similarly well-defined structure). There’s no <em>opportunity</em> to introduce generics, because we’re not dealing with a general algorithm.</p>

<p>So this begs the question: where <em>do</em> generics come in? How do I use my awesome <code class="language-plaintext highlighter-rouge">Codable</code> type with this framework? The answer is: the next layer of abstraction.</p>

<h2 id="hello-codable">Hello, Codable!</h2>

<p>Our HTTP stack deals with a concrete input type (<code class="language-plaintext highlighter-rouge">HTTPRequest</code>) and a concrete output type (<code class="language-plaintext highlighter-rouge">HTTPResponse</code>). There’s no place to put something generic there. We want generics at some point, because we want to use our nice <code class="language-plaintext highlighter-rouge">Codable</code> structs, but they don’t belong in the HTTP communication layer.</p>

<p>So, we’ll wrap up our <code class="language-plaintext highlighter-rouge">HTTPLoader</code> chain in a new layer that <em>can</em> handle generics. I call this the “Connection” layer, and it looks like this:</p>

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

    <span class="kd">private</span> <span class="k">let</span> <span class="nv">loader</span><span class="p">:</span> <span class="kt">HTTPLoader</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">loader</span> <span class="o">=</span> <span class="o">...</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">request</span><span class="p">(</span><span class="n">_</span> <span class="nv">request</span><span class="p">:</span> <span class="o">...</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span> <span class="o">...</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// TODO: create an HTTPRequest</span>
        <span class="c1">// TODO: interpret the HTTPResponse</span>
    <span class="p">}</span>

<span class="p">}</span>
</code></pre></div></div>

<p>In order to interpret a response in a generic way, <em>this</em> is where we’ll need generics, because this is the <em>algorithm</em> we need to make applicable to many different types. So, we’ll define a type that generically wraps an <code class="language-plaintext highlighter-rouge">HTTPRequest</code> and can interpret an <code class="language-plaintext highlighter-rouge">HTTPResponse</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Request</span><span class="o">&lt;</span><span class="kt">Response</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="k">let</span> <span class="nv">underlyingRequest</span><span class="p">:</span> <span class="kt">HTTPRequest</span>
    <span class="kd">public</span> <span class="k">let</span> <span class="nv">decode</span><span class="p">:</span> <span class="p">(</span><span class="kt">HTTPResponse</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">Response</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">underlyingRequest</span><span class="p">:</span> <span class="kt">HTTPRequest</span><span class="p">,</span> <span class="nv">decode</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">HTTPResponse</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">Response</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">underlyingRequest</span> <span class="o">=</span> <span class="n">underlyingRequest</span>
        <span class="k">self</span><span class="o">.</span><span class="n">decode</span> <span class="o">=</span> <span class="n">decode</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can also provide some convenience methods for when we know the <code class="language-plaintext highlighter-rouge">Response</code> is <code class="language-plaintext highlighter-rouge">Decodable</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Request</span> <span class="k">where</span> <span class="kt">Response</span><span class="p">:</span> <span class="kt">Decodable</span> <span class="p">{</span>

    <span class="c1">// request a value that's decoded using a JSON decoder</span>
    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">underlyingRequest</span><span class="p">:</span> <span class="kt">HTTPRequest</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">underlyingRequest</span><span class="p">:</span> <span class="n">underlyingRequest</span><span class="p">,</span> <span class="nv">decoder</span><span class="p">:</span> <span class="kt">JSONDecoder</span><span class="p">())</span>
    <span class="p">}</span>
    
    <span class="c1">// request a value that's decoded using the specified decoder</span>
    <span class="c1">// requires: import Combine</span>
    <span class="kd">public</span> <span class="kd">init</span><span class="o">&lt;</span><span class="kt">D</span><span class="p">:</span> <span class="kt">TopLevelDecoder</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">underlyingRequest</span><span class="p">:</span> <span class="kt">HTTPRequest</span><span class="p">,</span> <span class="nv">decoder</span><span class="p">:</span> <span class="kt">D</span><span class="p">)</span> <span class="k">where</span> <span class="kt">D</span><span class="o">.</span><span class="kt">Input</span> <span class="o">==</span> <span class="kt">Data</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">underlyingRequest</span><span class="p">:</span> <span class="n">underlyingRequest</span><span class="p">,</span>
                  <span class="nv">decode</span><span class="p">:</span> <span class="p">{</span> <span class="k">try</span> <span class="n">decoder</span><span class="o">.</span><span class="nf">decode</span><span class="p">(</span><span class="kt">Response</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">from</span><span class="p">:</span> <span class="nv">$0</span><span class="o">.</span><span class="n">body</span><span class="p">)</span> <span class="p">})</span>
    <span class="p">}</span>

<span class="p">}</span>
</code></pre></div></div>

<p>With this, we have a way to encapsulate the idea of “sending this <code class="language-plaintext highlighter-rouge">HTTPRequest</code> should result in a value I can decode using this closure”. We can now implement that <code class="language-plaintext highlighter-rouge">request</code> method we stubbed out earlier:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="kt">Connection</span> <span class="p">{</span> 
    <span class="o">...</span>

    <span class="kd">public</span> <span class="kd">func</span> <span class="n">request</span><span class="o">&lt;</span><span class="kt">ResponseType</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">request</span><span class="p">:</span> <span class="kt">Request</span><span class="o">&lt;</span><span class="kt">ResponseType</span><span class="o">&gt;</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">ResponseType</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="k">let</span> <span class="nv">task</span> <span class="o">=</span> <span class="kt">HTTPTask</span><span class="p">(</span><span class="nv">request</span><span class="p">:</span> <span class="n">request</span><span class="o">.</span><span class="n">underlyingRequest</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</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">response</span><span class="p">):</span>

                    <span class="k">do</span> <span class="p">{</span>
                        <span class="k">let</span> <span class="nv">response</span> <span class="o">=</span> <span class="k">try</span> <span class="n">request</span><span class="o">.</span><span class="nf">decode</span><span class="p">(</span><span class="nv">httpResponse</span><span class="p">:</span> <span class="n">response</span><span class="p">)</span>
                        <span class="nf">completion</span><span class="p">(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">response</span><span class="p">))</span>
                    <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
                        <span class="c1">// something when wrong while deserializing</span>
                        <span class="nf">completion</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="n">error</span><span class="p">))</span>
                    <span class="p">}</span>

                <span class="k">case</span> <span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span>
                    <span class="c1">// something went wrong during transmission (couldn't connect, dropped connection, etc)</span>
                    <span class="nf">completion</span><span class="p">(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="n">error</span><span class="p">))</span>
            <span class="p">}</span>
        <span class="p">})</span>
        <span class="n">loader</span><span class="o">.</span><span class="nf">load</span><span class="p">(</span><span class="n">task</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And using conditionalized extensions, we can make <code class="language-plaintext highlighter-rouge">Request</code> construction simple:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Request</span> <span class="k">where</span> <span class="kt">Response</span> <span class="o">==</span> <span class="kt">Person</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="kd">func</span> <span class="nf">person</span><span class="p">(</span><span class="n">_</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Request</span><span class="o">&lt;</span><span class="kt">Response</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">Request</span><span class="p">(</span><span class="nv">personID</span><span class="p">:</span> <span class="n">id</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="nf">init</span><span class="p">(</span><span class="nv">personID</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">request</span> <span class="o">=</span> <span class="kt">HTTPRequest</span><span class="p">(</span><span class="nv">path</span><span class="p">:</span> <span class="s">"/api/person/</span><span class="se">\(</span><span class="n">personID</span><span class="se">)</span><span class="s">/"</span><span class="p">)</span>

        <span class="c1">// because Person: Decodable, this will use the initializer that automatically provides a JSONDecoder to interpret the response</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">underlyingRequest</span><span class="p">:</span> <span class="n">request</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// usage:</span>
<span class="c1">// automatically infers `Request&lt;Person&gt;` based on the initializer/static method</span>
<span class="n">connection</span><span class="o">.</span><span class="nf">request</span><span class="p">(</span><span class="kt">Request</span><span class="p">(</span><span class="nv">personID</span><span class="p">:</span> <span class="mi">1</span><span class="p">))</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// or:</span>
<span class="n">connection</span><span class="o">.</span><span class="nf">request</span><span class="p">(</span><span class="o">.</span><span class="nf">person</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</code></pre></div></div>

<p>There are some important things at work here:</p>

<ul>
  <li>Remember that even a <code class="language-plaintext highlighter-rouge">404 Not Found</code> response is a <em>successful response</em>. It’s a response we got back from the server! <em>Interpreting</em> that response is a client-side problem. So by default, we can blindly attempt to deserialize <em>any</em> response, because every <code class="language-plaintext highlighter-rouge">HTTPResponse</code> is a “successful” response. That means dealing with a <code class="language-plaintext highlighter-rouge">404 Not Found</code> or <code class="language-plaintext highlighter-rouge">304 Not Modified</code> response is up to the client.</li>
  <li>By making each <code class="language-plaintext highlighter-rouge">Request</code> decode the response, we provide the opportunity for individualized/request-specific deserialization logic. One request might look for errors encoded in a JSON response if decoding fails, while another might just be satisfied with throwing a <code class="language-plaintext highlighter-rouge">DecodingError</code>.</li>
  <li>Since each <code class="language-plaintext highlighter-rouge">Request</code> uses a closure for decoding, we can capture domain- and contextually-specific values in the closure to aid in the decoding process for that particular request!</li>
  <li>We’re not limited to only JSON deserialization. Some requests might deserialize as JSON; others might deserialize using an <code class="language-plaintext highlighter-rouge">XMLDecoder</code> or something custom. Each request has the opportunity to decode a response however it wishes.</li>
  <li>Conditional extensions to <code class="language-plaintext highlighter-rouge">Request</code> mean we have a nice and expressive API of <code class="language-plaintext highlighter-rouge">connection.request(.person(42)) { ... }</code></li>
</ul>

<h2 id="hello-combine">Hello, Combine!</h2>

<p>This <code class="language-plaintext highlighter-rouge">Connection</code> layer also makes it easy to integrate with Combine. We can provide a method on <code class="language-plaintext highlighter-rouge">Connection</code> to expose sending a request and provide back a <code class="language-plaintext highlighter-rouge">Publisher</code>-conforming type to use in a publisher chain or as part of an <code class="language-plaintext highlighter-rouge">ObservableObject</code> or even with a <code class="language-plaintext highlighter-rouge">.onReceive()</code> modifier in SwiftUI:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">Combine</span>

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

    <span class="c1">// Future&lt;...&gt; is a Combine-provided type that conforms to the Publisher protocol</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="n">publisher</span><span class="o">&lt;</span><span class="kt">ResponseType</span><span class="o">&gt;</span><span class="p">(</span><span class="k">for</span> <span class="nv">request</span><span class="p">:</span> <span class="kt">Request</span><span class="o">&lt;</span><span class="kt">ResponseType</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">ResponseType</span><span class="p">,</span> <span class="kt">Error</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kt">Future</span> <span class="p">{</span> <span class="n">promise</span> <span class="k">in</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">request</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span> <span class="n">promise</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// This provides a "materialized" publisher, needed by SwiftUI's View.onReceive(...) modifier</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="n">publisher</span><span class="o">&lt;</span><span class="kt">ResponseType</span><span class="o">&gt;</span><span class="p">(</span><span class="k">for</span> <span class="nv">request</span><span class="p">:</span> <span class="kt">Request</span><span class="o">&lt;</span><span class="kt">ResponseType</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Future</span><span class="o">&lt;</span><span class="kt">Result</span><span class="o">&lt;</span><span class="kt">ResponseType</span><span class="p">,</span> <span class="kt">Error</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">return</span> <span class="kt">Future</span> <span class="p">{</span> <span class="n">promise</span> <span class="k">in</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">request</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="nf">promise</span><span class="p">(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="nv">$0</span><span class="p">))</span> <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

<span class="p">}</span>
</code></pre></div></div>

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

<p>We’ve finally reached the end! I hope you’ve enjoyed this series and that it’s opened your mind to new possibilities. Some things I hope you take away from this:</p>

<ul>
  <li>HTTP is not a scary, complex thing. At it’s core, it’s really really simple. It’s a simple <em>text-based</em> format for sending a request, and a simple format for getting a response. We can easily model that in Swift.</li>
  <li>Abstracting HTTP out to a high-level “request/response” model allows us to do some <em>really cool things</em> that would be really difficult to implement if we get stuck looking at all the <code class="language-plaintext highlighter-rouge">URLSession</code>-specific trees in the HTTP forest.</li>
  <li>We can have our cake and eat it too! This model of networking works great whether you’re using UIKit/AppKit or SwiftUI or whatever.</li>
  <li>By recognizing we didn’t need generics nor protocols, we avoided overly-complicating our code. Each part of the loader chain is discrete, composeable, and easily tested in isolation. We’ll never have to deal with those dreaded “associated type or self” errors while working in it.</li>
  <li>The principles of this approach work <em>regardless of your programming language and platform</em>. This series has been about “how to think about a problem”.</li>
</ul>

<p>Thanks for reading!</p>

<blockquote>
  <p>Do you have thoughts on the content of this series? Maybe you’ve found that some things work well with this approach, or things that don’t? I’d love to hear about your experience! Feel free to contact me <a href="/contact">via this site</a> or on <a href="https://twitter.com/davedelong">Twitter</a>.</p>
</blockquote>]]></content><author><name></name></author><category term="http" /><category term="swift" /><summary type="html"><![CDATA[Over the course of this series, we’ve started with a simple idea and taken it to some pretty fascinating places. The idea we started with is that a network layer can be abstracted out to the idea of “I send this request, and eventually I get a response”.]]></summary></entry></feed>