<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://jaredsinclair.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://jaredsinclair.com/" rel="alternate" type="text/html" /><updated>2026-05-16T03:18:33+00:00</updated><id>https://jaredsinclair.com/feed.xml</id><title type="html">Jared Sinclair</title><subtitle>Write an awesome description for your new site here. You can edit this line in _config.yml. It will appear in your document head meta (for Google search results) and in your feed.xml site description.</subtitle><entry><title type="html">We Need to Talk About Observation</title><link href="https://jaredsinclair.com/2025/09/10/observation.html" rel="alternate" type="text/html" title="We Need to Talk About Observation" /><published>2025-09-10T11:00:00+00:00</published><updated>2025-09-10T11:00:00+00:00</updated><id>https://jaredsinclair.com/2025/09/10/observation</id><content type="html" xml:base="https://jaredsinclair.com/2025/09/10/observation.html"><![CDATA[<p><em>(Note: Every buzz-and-nonbuzz word of this post was painstakingly conceived and typed by hand using a real-ass keyboard and a real-ass text editor running on my personal computer in my home office outside Cleveland, Ohio. No GPUs were harmed in the making of this film.)</em></p>

<h3 id="tldr">TL;DR</h3>

<p>For the Impatient of Spirit among you: Apple has effectively deprecated the reigning paradigm of the <a href="https://developer.apple.com/documentation/combine/observableobject">ObservableObject</a> protocol and <a href="https://developer.apple.com/documentation/combine/published">@Published</a> properties observed via the Combine framework, but they’ve only partially provided its replacement via the <a href="https://developer.apple.com/documentation/observation/observable()">@Observable</a> macro and the <a href="https://developer.apple.com/documentation/observation/withobservationtracking(_:onchange:)">withObservationTracking</a> free function. The gaps between the old way and the new way are worth careful consideration.</p>

<h3 id="out-with-the-old">Out with the old…</h3>

<p>Since the advent first of the React framework and then later other similar projects—SwiftUI, Jetpack Compose, etc.—the peppiest and most responsive apps these days are written as thin layers of declarative UI code that spontaneously react to changes in thick layers of object-oriented code. Those latter objects are sometimes called “view models” or “flow coordinators” or whatever you wish to call them. Naming aside, the reactive paradigm on Apple platforms has been reified by SwiftUI. It has usurped UIKit/AppKit as the dominant framework for new UI development.</p>

<p>What I find most exciting about SwiftUI isn’t just the fact that it’s declarative. What’s most exciting is the other half of my codebase. I’m excited about all the stuff that <em>isn’t</em> SwiftUI. Business logic. View models. <em>UserProviderManagerHamburgerHelper</em>. Files that only have <code class="language-plaintext highlighter-rouge">import Foundation</code> at the top and nothing else. That stuff gets written entirely differently in a pure SwiftUI world, relative to how it was written for UIKit/AppKit. In the relatively-before-times, in the kinda-sorta-long-long-ago, there were DataSources and Delegates, IBOutlets and IBActions. Stuff worked like an old telephone switchboard: somebody had to plug the <code class="language-plaintext highlighter-rouge">dataSource</code> cable into the <em>SomethingSomethingDataSource</em> jack in a wall of such holes. If anything worked, its because a giant tangle of loosely coupled, weakly-referenced properties got wired up just-in-time.</p>

<p>Ignoring for now, because we will discuss them below, the <code class="language-plaintext highlighter-rouge">@Observable</code> macro and the <em>Observation</em> framework, contemporary application code that I regard as Pure and Faultless is this: a thin shell of incredibly dumb UI code wrapping <a href="https://www.youtube.com/watch?v=yTkzNHF6rMs">a core of intelligent business logic</a> factored into a constellation of focused, domain-specific objects:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@MainActor</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">MyModel</span><span class="p">:</span> <span class="kt">ObservableObject</span> <span class="p">{</span>
    <span class="kd">@Published</span> <span class="k">var</span> <span class="nv">stuff</span> <span class="o">=</span> <span class="kt">Stuff</span><span class="o">.</span><span class="nf">initialStuff</span><span class="p">()</span>
    <span class="kd">@Published</span> <span class="k">var</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">PresentableError</span><span class="p">?</span>
    <span class="k">var</span> <span class="nv">disableNextButton</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="cm">/* hard stuff */</span> <span class="p">}</span>
    <span class="kd">func</span> <span class="nf">next</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* complex stuff */</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">MyView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@StateObject</span> <span class="k">var</span> <span class="nv">model</span> <span class="o">=</span> <span class="kt">MyModel</span><span class="p">()</span>
    
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">StuffEditor</span><span class="p">(</span><span class="nv">stuff</span><span class="p">:</span> <span class="err">$</span><span class="n">model</span><span class="o">.</span><span class="n">stuff</span><span class="p">)</span>
        <span class="kt">Button</span><span class="p">(</span><span class="s">"Next"</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">model</span><span class="o">.</span><span class="nf">next</span><span class="p">()</span>
        <span class="p">}</span><span class="o">.</span><span class="nf">disabled</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">disableNextButton</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I’ll select one thing to highlight in the above sample code: notice that the “Next” button action is just <code class="language-plaintext highlighter-rouge">model.next()</code>. There’s no need to <code class="language-plaintext highlighter-rouge">guard model.stuff.isValidAndEverything else { return }</code>, because that’s handled by <code class="language-plaintext highlighter-rouge">.disabled(model.disableNextButton)</code>. My view is exceptionally easy to discard and rewrite for Liquid Glass without having to rewrite and retest any business logic. This kind of code is easy to write and maintain, and is wine to you, wine and comfort, if you care about shipping new stuff fast and good and cheap, all three.</p>

<p>I want to show one more example before I get into how Apple has taken an axe to major portions of this approach.</p>

<p>It’s not just declarative UI that has been enabled by the reactive pattern. It’s also the behaviors that spring up between non-UI objects. Once an app reaches a sufficient level of real-world complexity, your app will have all kinds of important relationships:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// View relying on model:
view &lt;---- object

// Object relying on delegate/data-source:
object &lt;---- object

// One-to-many observations:
object ----&gt; [object, object, object, ...]
</code></pre></div></div>

<p>It’s that third one that has been accelerated in recent years by the Combine framework, the ObservableObject protocol, and the @Published property wrapper.</p>

<p>I’ve written and reviewed no shortage of code that looks a lot like this contrived beauty (which I have shorn of Swift’s concurrency isolation grievances for brevity):</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@MainActor</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">UserCoordinator</span><span class="p">:</span> <span class="kt">ObservableObject</span> <span class="p">{</span>
    <span class="kd">@Published</span> <span class="kd">private(set)</span> <span class="nv">user</span><span class="p">:</span> <span class="kt">User</span><span class="p">?</span>
<span class="p">}</span>

<span class="kd">@MainActor</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">SyncEngine</span><span class="p">:</span> <span class="kt">ObservableObject</span> <span class="p">{</span>
    <span class="kd">@Published</span> <span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">status</span><span class="p">:</span> <span class="kt">Status</span> <span class="o">=</span> <span class="o">.</span><span class="n">idle</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">cancellables</span><span class="p">:</span> <span class="kt">Set</span><span class="o">&lt;</span><span class="kt">AnyCancellable</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">[]</span>
    
    <span class="kd">func</span> <span class="nf">observe</span><span class="p">(</span><span class="n">_</span> <span class="nv">coordinator</span><span class="p">:</span> <span class="kt">UserCoordinator</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">coordinator</span><span class="o">.</span><span class="err">$</span><span class="n">user</span>
            <span class="o">.</span><span class="nf">removeDuplicates</span><span class="p">()</span>
            <span class="o">.</span><span class="n">sink</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="n">user</span> <span class="k">in</span>
                <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="nf">restartOrCancelSync</span><span class="p">(</span><span class="nv">newUser</span><span class="p">:</span> <span class="n">user</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">subscriptions</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I was able to write the above without bothering to switch to Xcode from my preferred Markdown editor to get the syntax correct. It’s an easy pattern to reproduce, and it works alongside SwiftUI in a quietly supportive fashion. Need a view that displays the sync progress? Just pass a reference to <code class="language-plaintext highlighter-rouge">SyncEngine</code> to your view and connect a ProgressView to that <code class="language-plaintext highlighter-rouge">status</code> property, bada-bing. Important: you do <strong>not</strong> need a View in order for the SyncEngine to do its job. The SyncEngine is fully capable of subscribing to changes to the <code class="language-plaintext highlighter-rouge">user</code> property of the UserCoordinator and <strong>programmatically, spontaneously</strong> respond to the activity of the UserCoordinator, without either the UI or the UserCoordinator needing any awareness of this behavior.</p>

<p>It just works.</p>

<p>But Apple has taken an axe to all that.</p>

<h3 id="and-in-with-the-new">…and in with the new</h3>

<p>Combine has been softly deprecated since structured concurrency was debuted, more softly in iOS 13 when AsyncSequence was released, but even more so over the subsequent years with improvements to structured concurrency and the introduction of the @Observable macro in iOS 17. It is now possible to subscribe to long-running streams of elements emitted asynchronously using structured concurrency:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">subscribe</span><span class="o">&lt;</span><span class="kt">S</span><span class="o">&gt;</span><span class="p">(</span>
    <span class="n">to</span> <span class="nv">sequence</span><span class="p">:</span> <span class="kt">S</span>
<span class="p">)</span> <span class="k">async</span> <span class="k">where</span> <span class="kt">S</span><span class="p">:</span> <span class="kt">AsyncSequence</span><span class="p">,</span> <span class="kt">S</span><span class="o">.</span><span class="kt">Element</span> <span class="o">=</span> <span class="kt">User</span><span class="p">?,</span> <span class="kt">S</span><span class="o">.</span><span class="kt">Failure</span> <span class="o">=</span> <span class="kt">Never</span> <span class="p">{</span>
    <span class="k">for</span> <span class="k">await</span> <span class="n">user</span> <span class="k">in</span> <span class="n">sequence</span> <span class="p">{</span>
        <span class="nf">restartOrCancelSync</span><span class="p">(</span><span class="nv">newUser</span><span class="p">:</span> <span class="n">user</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The @Observable macro has usurped ObservableObject as the <em>de rigeur</em> way to write a “view model” type of object:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@MainActor</span> <span class="kd">@Observable</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">MyModel</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">stuff</span> <span class="o">=</span> <span class="kt">Stuff</span><span class="o">.</span><span class="nf">initialStuff</span><span class="p">()</span>
    <span class="k">var</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">PresentableError</span><span class="p">?</span>
    <span class="k">var</span> <span class="nv">disableNextButton</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="cm">/* hard stuff */</span> <span class="p">}</span>
    <span class="kd">func</span> <span class="nf">next</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* complex stuff */</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">MyView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="k">var</span> <span class="nv">model</span> <span class="o">=</span> <span class="kt">MyModel</span><span class="p">()</span>
    
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">StuffEditor</span><span class="p">(</span><span class="nv">stuff</span><span class="p">:</span> <span class="err">$</span><span class="n">model</span><span class="o">.</span><span class="n">stuff</span><span class="p">)</span>
        <span class="kt">Button</span><span class="p">(</span><span class="s">"Next"</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">model</span><span class="o">.</span><span class="nf">next</span><span class="p">()</span>
        <span class="p">}</span><span class="o">.</span><span class="nf">disabled</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">disableNextButton</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There are a ton of problems with that code, as-written. It might compile, but it wouldn’t pass code review if you ran it past me at work. But before I get to that, let’s talk about what’s <strong>good</strong> about it, because I do appreciate the ways in which this new stuff is a step forward:</p>

<ul>
  <li><strong>Breathing room:</strong> - You don’t need a ton of <code class="language-plaintext highlighter-rouge">@Published</code> property wrappers. Observability is opt-out, not opt-in. Also (not pictured here), if your view doesn’t need editing capability, you can use a plain-old <code class="language-plaintext highlighter-rouge">let</code> property without the <code class="language-plaintext highlighter-rouge">@State</code>.</li>
  <li><strong>Optimization:</strong> - Only the properties of an @Observable object that are accessed by your little slice of the view hierarchy are invalidated during a UI update. The old thing was a big, blunt hammer that incurred too many unnecessary redraws.</li>
  <li><strong>Nesting</strong> - If you want to refactor MyModel to be a composition of several smaller @Observable objects, you can do that without having to rewrite your view hierarchy. This is huge.</li>
</ul>

<p>But there are the problems with the code as I’ve written it above. Exploring those problems, and the paucity of their solutions, will shed light on the shortcomings in the new status quo:</p>

<ul>
  <li>
    <p><strong>Needless object recreation.</strong> - See that line where the <code class="language-plaintext highlighter-rouge">@State</code> property replaced the old <code class="language-plaintext highlighter-rouge">@StateObject</code>? That’s more than cosmetic. StateObject memoized its initialization via an escaping autoclosure over the default value, so that over the view’s lifetime on-screen, only one instance of the model would have been instantiated. The State wrapper offers no such feature. Anything that causes the MyView struct (not the on-screen view it represents, the struct itself, easy to misunderstand) to be recomputed by its ancestors—which is astonishingly easy in any real-world application—will discard the old instance and create a new one. I’ve seen this happen a lot with form validation and keyboards. At worst, a view model gets <code class="language-plaintext highlighter-rouge">r-e-c-r-e-a-t-e-d</code> on every keypress. If you’ve got anything expensive happening inside that view model’s <code class="language-plaintext highlighter-rouge">init</code> method, buddy: watch out.</p>
  </li>
  <li>
    <p><strong>No streamlined way to avoid object recreation.</strong> - If you want to avoid that needless recreation, and bring back that old StateObject behavior, <a href="https://developer.apple.com/documentation/swiftui/state#Store-observable-objects">Apple’s recommendation has been</a>—and still is even in OS 26!—to do the following tedious <code class="language-plaintext highlighter-rouge">onAppear</code> modifier dance:</p>
  </li>
</ul>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">model</span><span class="p">:</span> <span class="kt">Model</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="n">content</span><span class="o">.</span><span class="n">onAppear</span> <span class="p">{</span>
            <span class="k">if</span> <span class="n">model</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
                <span class="n">model</span> <span class="o">=</span> <span class="kt">Model</span><span class="p">()</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="kd">@ViewBuilder</span> <span class="k">var</span> <span class="nv">content</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">let</span> <span class="nv">model</span> <span class="p">{</span>
            <span class="kt">MyActualView</span><span class="p">(</span><span class="nv">model</span><span class="p">:</span> <span class="err">$</span><span class="n">model</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="kt">Color</span><span class="o">.</span><span class="n">clear</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>That’s very difficult to generalize. There ain’t no way to write a property wrapper to do it. At best, you can write a reusable generic <code class="language-plaintext highlighter-rouge">View</code> that takes two blocks: one that instantiates the state and another that provides the view. However you solve it, it is tedious and annoying to write code like this. But it’s totally necessary if your <code class="language-plaintext highlighter-rouge">Model</code> class, unlike my example above, requires any initialization parameters—which it should, because you ought not to be using networking and database singletons.</p>

<p>But I have a bigger problem with this new paradigm, and it isn’t visible from a UI code sample.</p>

<h3 id="where-did-you-go-programmatic-observation">Where did you go, programmatic observation?</h3>

<p>Remember I wrote this above:</p>

<blockquote>
  <p>What’s most exciting is the other half of my codebase.</p>
</blockquote>

<p>In a sufficiently complex, real-world application, the thin layers of SwiftUI code are only half the picture. There is usually, if you’re separating concerns, a constellation of domain-specific objects that perform duties that aren’t user-visible or aren’t visible right away. These duties often require establishing one-to-one or one-to-many observation from one object to other objects that it knows nothing about. In the world of the ObservableObject protocol and @Published properties, the <strong>exact same</strong> mechanism that powers the relationship between an object and a view also powers the relationship between an object and another object. But with the @Observable macro, the object-to-object relationship is different. Much different.</p>

<p>In my opinion, it’s undercooked.</p>

<h3 id="ios-17-withobservationtracking-comes-withgreatresponsibility">iOS 17: withObservationTracking comes withGreatResponsibility</h3>

<p>In iOS 17 the <code class="language-plaintext highlighter-rouge">withObservationTracking</code> free function is the <strong>only</strong> way for something to subscribe to changes to an @Observable macro’ed object:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">withObservationTracking</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">apply</span><span class="p">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">T</span><span class="p">,</span>
    <span class="nv">onChange</span><span class="p">:</span> <span class="kd">@autoclosure</span> <span class="p">()</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="o">-&gt;</span> <span class="kt">T</span>
</code></pre></div></div>

<p>It’s behavior may really surprise you, if your previous mental model has been shaped, like mine has, by Combine. Let’s break it down:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">withObservationTracking</code> this is a synchronous function without any Swift concurrency isolation sugar except that which you get by default for nonisolated, synchronous functions.</li>
  <li><code class="language-plaintext highlighter-rouge">apply</code> This is a synchronous function parameter that is invoked exactly once, immediately. The value returned from this function is the <code class="language-plaintext highlighter-rouge">T</code> returned from <em>withObservationTracking</em>. You don’t actually have to return anything but <code class="language-plaintext highlighter-rouge">Void</code> if you don’t need a return value. But you must access properties of your @Observed object(s) inside <code class="language-plaintext highlighter-rouge">apply</code>. Only the properties that are accessed will be tracked for future changes. It is sufficient just to <code class="language-plaintext highlighter-rouge">print(myModel.value)</code> inside the <code class="language-plaintext highlighter-rouge">apply</code> if that’s all you need. It is headscratchingly difficult to grok how to correctly implement an <code class="language-plaintext highlighter-rouge">apply</code> body if you’re bringing a Combine mental model to the table.</li>
  <li><code class="language-plaintext highlighter-rouge">onChange</code> This is a synchronous, escaping, autoclosed function. It will be called <strong>either once or zero times, and no more</strong>. It is <em>not</em> like a Combine <code class="language-plaintext highlighter-rouge">sink</code>, which is called upon every emitted value. It is called either zero times (if the observed object never changes the properties you accessed in <code class="language-plaintext highlighter-rouge">apply</code>) or exactly once (if one or more of those properties change in the future).</li>
</ul>

<p>If you want to have sustained, ongoing observation of all future changes, then you have to add a recursive tail-call to the <code class="language-plaintext highlighter-rouge">onChange</code> that (probably reëntrantly) calls <code class="language-plaintext highlighter-rouge">withObservationTracking</code> again.</p>

<p>Returning to my example above of a SyncEngine observing a UserCoordinator, let’s look at how this would work with @Observable:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@MainActor</span> <span class="kd">@Observable</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">UserCoordinator</span> <span class="p">{</span>
    <span class="kd">private(set)</span> <span class="nv">user</span><span class="p">:</span> <span class="kt">User</span><span class="p">?</span>
<span class="p">}</span>

<span class="kd">@MainActor</span> <span class="kd">@Observable</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">SyncEngine</span> <span class="p">{</span>
    <span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">status</span><span class="p">:</span> <span class="kt">Status</span> <span class="o">=</span> <span class="o">.</span><span class="n">idle</span>
    
    <span class="kd">func</span> <span class="nf">observe</span><span class="p">(</span><span class="n">_</span> <span class="nv">coordinator</span><span class="p">:</span> <span class="kt">UserCoordinator</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">withObservationTracking</span> <span class="p">{</span>
            <span class="nf">restartOrCancelSync</span><span class="p">(</span><span class="nv">newUser</span><span class="p">:</span> <span class="n">coordinator</span><span class="o">.</span><span class="n">user</span><span class="p">)</span>
        <span class="p">}</span> <span class="nv">onChange</span><span class="p">:</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">,</span> <span class="k">weak</span> <span class="n">coordinator</span><span class="p">]</span> <span class="k">in</span>
            <span class="c1">// recursive tail-call:</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span><span class="p">,</span> <span class="k">let</span> <span class="nv">coordinator</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span><span class="n">coordinator</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What’s missing from that picture?</p>

<ul>
  <li>
    <p><strong>Discoverability</strong> - It isn’t easy to understand how to use this function. It’s a free function, not a <code class="language-plaintext highlighter-rouge">$member</code> of the object you’re trying to observe, which makes it harder to discover.</p>
  </li>
  <li>
    <p><strong>Cancellation</strong> - How do I cancel my observation? How many times will this run? With Combine, you were given an explicit <code class="language-plaintext highlighter-rouge">AnyCancellable</code> from the <code class="language-plaintext highlighter-rouge">sink</code> method. Your subscription lived until you either called <code class="language-plaintext highlighter-rouge">cancel()</code> or discarded the object. There’s no such thing returned here. You might wonder then if you’re supposed to use structured concurrency, <code class="language-plaintext highlighter-rouge">Task.cancel()</code>, but that’s not it, either. There’s no interplay with structured concurrency here. The answer is this: “cancellation” means “don’t do a recursive tail-call”, but that isn’t the same thing as cancellation. Your subscription, so-called, is always a one-shot, and it never resolves until the next time the <code class="language-plaintext highlighter-rouge">onChange</code> handler fires, which might never happen. If you don’t want to observe changes anymore, you have to do something like (a) rely on <code class="language-plaintext highlighter-rouge">[weak self]</code> to cause your object to disappear, and/or (b) add a <code class="language-plaintext highlighter-rouge">private var dontObserveAnymore: Bool</code> property to your object and set that dirty bit to <code class="language-plaintext highlighter-rouge">true</code> to prevent future recursive tail-calls via, like, a <code class="language-plaintext highlighter-rouge">guard</code> or something. It’s entirely up to you. There is no established pattern, and definitely no off-the-shelf API guiding your hand.</p>
  </li>
</ul>

<p>It all feels phoned-in, hardly the replacement for the opinionated, curated set of public APIs offered by the Combine framework.</p>

<h3 id="ios-26-observations-struct">iOS 26: Observations struct</h3>

<p>New in iOS/macOS/etc 26 (technically new in Swift 6.2, but since the runtime no longer gets embedded in binaries, it’s unavailable prior to OS 26), there is now the Observations struct. Don’t confuse it with the <em>Observation</em> package to which it belongs (don’t miss that dangling, plural ess…)</p>

<p>Observations is an Apple-provided way for one object to subscribe to long-running changes to some other, @Observable-macro’ed object. It is written to use the <a href="https://developer.apple.com/documentation/Swift/AsyncSequence">AsyncSequence protocol</a>. Despite the fact that <code class="language-plaintext highlighter-rouge">withObservationTracking</code> was released in OS 17, Observations has not been back-ported and requires OS 26.</p>

<p>Usage of Observations is like any other AsyncSequence. You use the <code class="language-plaintext highlighter-rouge">for in await</code> keywords. Here’s my SyncEngine example, rewritten to use Observations:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@MainActor</span> <span class="kd">@Observable</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">UserCoordinator</span> <span class="p">{</span>
    <span class="kd">private(set)</span> <span class="nv">user</span><span class="p">:</span> <span class="kt">User</span><span class="p">?</span>
<span class="p">}</span>

<span class="kd">@MainActor</span> <span class="kd">@Observable</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">SyncEngine</span> <span class="p">{</span>
    <span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">status</span><span class="p">:</span> <span class="kt">Status</span> <span class="o">=</span> <span class="o">.</span><span class="n">idle</span>
    
    <span class="kd">func</span> <span class="nf">observe</span><span class="p">(</span><span class="n">_</span> <span class="nv">coordinator</span><span class="p">:</span> <span class="kt">UserCoordinator</span><span class="p">)</span> <span class="k">async</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">obs</span> <span class="o">=</span> <span class="kt">Observations</span><span class="o">.</span><span class="n">untilFinished</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="n">coordinator</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">coordinator</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="o">.</span><span class="n">finish</span> <span class="p">}</span>
            <span class="k">return</span> <span class="n">coordinator</span><span class="o">.</span><span class="n">user</span>
        <span class="p">}</span>
        <span class="k">for</span> <span class="k">await</span> <span class="n">user</span> <span class="k">in</span> <span class="n">obs</span> <span class="p">{</span>
            <span class="nf">restartOrCancelSync</span><span class="p">(</span><span class="nv">newUser</span><span class="p">:</span> <span class="n">user</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 mix of steps forward and steps backward. I’ll start with the positive:</p>

<h4 id="the-positive">The Positive</h4>

<ul>
  <li>
    <p><strong>Structured Concurrency</strong> - The new API is written with structured concurrency in mind. The closure passed to <code class="language-plaintext highlighter-rouge">Observation.untilFinished</code> (as well as to the unbounded <code class="language-plaintext highlighter-rouge">init</code> alternative) is annotated with <code class="language-plaintext highlighter-rouge">@isolated(any)</code> and <code class="language-plaintext highlighter-rouge">@_inheritActorContext</code> and other goodies that make it play nicely even in Swift 6 language mode. You can stop your stream from emitting subsequent values by either throwing an error or returning <code class="language-plaintext highlighter-rouge">.finish</code>. More on the topic of cancellation and stream-stopping below, it’s not all rosy.</p>
  </li>
  <li>
    <p><strong>Generics</strong> - The struct is generic over both the elements it produces and the errors it throws. This lends itself to terse, idiomatic Swift code. The <code class="language-plaintext highlighter-rouge">withObservationTracking</code> function didn’t allow for throwing errors at all.</p>
  </li>
</ul>

<p>But there are shortcomings with this new API, some of which are particularly gnarly to resolve.</p>

<h4 id="the-gnarly">The Gnarly</h4>

<p>The gnarly bits are really just one, big, gnarly problem with multiple facets: it is hard to reason about the lifetime of the Observations async sequence and the lifetimes of the objects involved. But I’ll try to separate out those problems here because I like bulleted lists.</p>

<ul>
  <li><strong>Cancellation</strong> - It still isn’t as clear, compared to Combine’s <code class="language-plaintext highlighter-rouge">AnyCancellable</code>, how you’re supposed to cancel an Observations struct. There’s no obvious visible API surface for doing so. You might wonder if Task cancellation is the way. You would be correct. To implement cancellation you need to wrap the entire thing in a Task, store that task in an instance variable, and determine key points in the lifecycle of your object to cancel that task:</li>
</ul>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@MainActor</span> <span class="kd">@Observable</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">SyncEngine</span> <span class="p">{</span>
    <span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">status</span><span class="p">:</span> <span class="kt">Status</span> <span class="o">=</span> <span class="o">.</span><span class="n">idle</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">userObservation</span><span class="p">:</span> <span class="kt">Task</span><span class="o">&lt;</span><span class="kt">Void</span><span class="p">,</span> <span class="n">any</span> <span class="kt">Error</span><span class="o">&gt;</span><span class="p">?</span>
    
    <span class="kd">deinit</span> <span class="p">{</span>
        <span class="n">userObservation</span><span class="p">?</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">observe</span><span class="p">(</span><span class="n">_</span> <span class="nv">coordinator</span><span class="p">:</span> <span class="kt">UserCoordinator</span><span class="p">)</span> <span class="k">async</span> <span class="p">{</span>
        <span class="n">userObservation</span> <span class="o">=</span> <span class="kt">Task</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">let</span> <span class="nv">coordinator</span> <span class="o">=</span> <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">coordinator</span>
            <span class="k">let</span> <span class="nv">obs</span> <span class="o">=</span> <span class="kt">Observations</span><span class="o">.</span><span class="n">untilFinished</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="n">coordinator</span><span class="p">]</span> <span class="k">in</span>
                <span class="k">guard</span> <span class="k">let</span> <span class="nv">coordinator</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="o">.</span><span class="n">finish</span> <span class="p">}</span>
                <span class="k">return</span> <span class="n">coordinator</span><span class="o">.</span><span class="n">user</span>
            <span class="p">}</span>
            <span class="k">for</span> <span class="k">await</span> <span class="n">user</span> <span class="k">in</span> <span class="n">obs</span> <span class="p">{</span>
                <span class="k">if</span> <span class="k">self</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
                    <span class="k">throw</span> <span class="kt">CancellationError</span><span class="p">()</span>
                <span class="p">}</span>
                <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="nf">restartOrCancelSync</span><span class="p">(</span><span class="nv">newUser</span><span class="p">:</span> <span class="n">user</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>There are problems with the above code, though, and I don’t recommend doing things exactly that way.</p>

<ul>
  <li>
    <p><strong>Object lifetimes</strong> - It is important that your <code class="language-plaintext highlighter-rouge">Task</code> and your <code class="language-plaintext highlighter-rouge">Observations</code> structs weakly-capture both <code class="language-plaintext highlighter-rouge">self</code> and whatever object you’re trying to observe. But it’s still easy to get it wrong. It just as hard here as it was in the “old” days of Objective-C block capture semantics. A variable captured weakly in an outer scope can still live longer than you wish. See how I have that statement inside the Task <code class="language-plaintext highlighter-rouge">let coordinator = self?.coordinator</code>? That local variable <code class="language-plaintext highlighter-rouge">coordinator</code> is going to remain in memory for as long as the Task body has lexical scope, and that is as long as the <code class="language-plaintext highlighter-rouge">await</code> down below is still waiting on the next value, which is potentially <strong>forever</strong> if you’ve thus created a retain cycle between the Task that is sustaining the lifetime of the coordinator and the coordinator, never producing another value, sustaining the Task.</p>
  </li>
  <li>
    <p><strong>Actor isolation hassles with cancellation</strong> - That <code class="language-plaintext highlighter-rouge">deinit</code> method isn’t isolated to the Main Actor, not yet. You can’t access a mutable <code class="language-plaintext highlighter-rouge">var</code> property from the deinit. You would need to wrap that property in some kind of synchronization box, like a <a href="https://developer.apple.com/documentation/synchronization/mutex">Mutex</a>, which comes with its own kinds of hassles and boilerplate.</p>
  </li>
</ul>

<p>Side note: <a href="https://github.com/swiftlang/swift/blob/main/stdlib/public/Observation/Sources/Observation/Observations.swift">the source code for Observations</a> is open to public view. If you squint hard enough, you can figure out that it’s basically a gigantic, <a href="https://memory-alpha.fandom.com/wiki/V%27ger">V-Ger</a> like edifice around <code class="language-plaintext highlighter-rouge">withObservationTracking</code> with recursive tail-calls handled as a combination of state machinery and the AsyncSequence protocol. I find it simultaneously (concurrently?) both nifty and somewhat deflating that Apple’s solution to programmatic observation of @Observable macro objects is still propped up by the ho-hum <code class="language-plaintext highlighter-rouge">withObservationTracking</code> free function.</p>

<p>Alas.</p>

<h3 id="what-weve-lost">What We’ve Lost</h3>

<p>It’s worth comparing the Observations example above (the version where I wrapped it in a Task and manually managed cancellation inside a <code class="language-plaintext highlighter-rouge">deinit</code> method) versus the exact same equivalent behavior implemented via Combine observation of a @Published property:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="k">var</span> <span class="nv">cancellables</span><span class="p">:</span> <span class="kt">Set</span><span class="o">&lt;</span><span class="kt">AnyCancellable</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">[]</span>
    
<span class="kd">func</span> <span class="nf">observe</span><span class="p">(</span><span class="n">_</span> <span class="nv">coordinator</span><span class="p">:</span> <span class="kt">UserCoordinator</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">coordinator</span><span class="o">.</span><span class="err">$</span><span class="n">user</span>
        <span class="o">.</span><span class="n">sink</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="n">user</span> <span class="k">in</span>
          <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="nf">restartOrCancelSync</span><span class="p">(</span><span class="nv">newUser</span><span class="p">:</span> <span class="n">user</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">subscriptions</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Look how much more succinct that is! And how little it relies on needing to grok whatever is going on with an enclosing structured concurrency Task from which <code class="language-plaintext highlighter-rouge">observe(_:)</code> happens to be called.</p>

<p>With Combine (a) you don’t have to override <code class="language-plaintext highlighter-rouge">deinit</code>, (b) you don’t have to specialize the <code class="language-plaintext highlighter-rouge">cancellables</code> property with generics which means (c) you can store multiple subscriptions in a single property with ease, (d) you don’t have to remember to weakly-reference the object being observed because it’s done for you, (e) AnyCancellable does the minimum-viable thing for you automatically by cancelling itself when destroyed, which (f) in the majority of use cases is more than good enough to limit Combine observation streams to the correct duration.</p>

<h3 id="further-recommendations">Further recommendations</h3>

<p>Even despite all my qualms, I remain convinced that the @Observable macro is the right path forward, both for Apple and their third-party developer community. I really appreciate the ways that it works with SwiftUI (and, worth a note in passing, Swift Data). I just wish that Apple would have been as opinionated about non-UI programming when designing the public APIs for @Observable as they were with the Combine framework. This new stuff feels penciled in and incremental, which would be OK if it weren’t absolutely fundamental to key architectural choices we have to make when building an app.</p>

<p>When you’re choosing between ObservableObject and @Observable, you should not limit the scope of your consideration solely to what’s best for SwiftUI. The choice you make will have structural implications on non-UI code.</p>

<p>Understand what you’re building before you build it.</p>

<h3 id="corrections">Corrections</h3>

<ul>
  <li>I previously misidentified AsyncSequence as being released in iOS 18. It was released in iOS 13. (Thanks <a href="https://mastodon.social/@rdsquared@mstdn.social">rdsquared</a>).</li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[(Note: Every buzz-and-nonbuzz word of this post was painstakingly conceived and typed by hand using a real-ass keyboard and a real-ass text editor running on my personal computer in my home office outside Cleveland, Ohio. No GPUs were harmed in the making of this film.)]]></summary></entry><entry><title type="html">Beware @unchecked Sendable, or Watch Out for Counterintuitive Implicit Actor-Isolation</title><link href="https://jaredsinclair.com/2024/11/12/beware-unchecked.html" rel="alternate" type="text/html" title="Beware @unchecked Sendable, or Watch Out for Counterintuitive Implicit Actor-Isolation" /><published>2024-11-12T11:00:00+00:00</published><updated>2024-11-12T11:00:00+00:00</updated><id>https://jaredsinclair.com/2024/11/12/beware-unchecked</id><content type="html" xml:base="https://jaredsinclair.com/2024/11/12/beware-unchecked.html"><![CDATA[<p>I ran into some unexpected runtime crashes recently while testing an app on iOS 18 compiled under Swift 6 language mode, and the root causes ended up being the perils of using <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code> in combination with some counterintuitive compiler behavior with implicit actor isolation. Rather than start at the end, I’ll walk you through how I introduced the crash, and then what I did to resolve it.</p>

<p class="inline-image"><a href="https://jaredsinclair.com/assets/img/false-positives.png"><img src="https://jaredsinclair.com/assets/img/false-positives.png" /></a></p>

<p>Let’s say you’re maintaining a Swift package that’s used across a number of different applications. Your library does nontrivial work and therefore your consumers need access to log messages coming from your package. Since your package cannot know how the host application wishes to capture logs, you decide to provide a public API surface that, at runtime, allows the host application to configure a logging “sink” through which all log messages from your package will flow:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">enum</span> <span class="kt">Logging</span> <span class="p">{</span>
    <span class="c1">/// Replace the value of `sink` with a block that routes</span>
    <span class="c1">/// log output to your app's preferred destination:</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="k">var</span> <span class="nv">sink</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">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>But this will not compile under Swift 6 language mode (or under Swift 5 language mode with additional concurrency checks enabled):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Static property 'sink' is not concurrency-safe because it is nonisolated global shared mutable state
</code></pre></div></div>

<p>Womp-womp. As the maintainer of my Swift package, since Swift 4 I have been doing the legwork in the real world, through documentation and code review, to make sure that none of my consumers are mutating the <code class="language-plaintext highlighter-rouge">sink</code> property except exactly once, early during app launch, before anything could be accessing it. It’s a mitigated risk. But under Swift 6, these kinds of risk mitigations are no longer sufficient. A fix has to be made in the code. Pinky swears no longer cut it.</p>

<p>One option could be to convert the <code class="language-plaintext highlighter-rouge">Logging</code> API to something with explicit global actor isolation, say via the Main Actor:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">enum</span> <span class="kt">Logging</span> <span class="p">{</span>
    <span class="c1">// I added `@MainActor` below:</span>
    <span class="kd">@MainActor</span> <span class="kd">public</span> <span class="kd">static</span> <span class="k">var</span> <span class="nv">sink</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">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I wouldn’t recommend this option unless all the logging in your package is already coming from code isolated to the main actor. Otherwise, you’ll have to edit many, many call sites from a synchronous call:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">foo</span><span class="p">()</span>
<span class="kt">Logging</span><span class="o">.</span><span class="nf">sink</span><span class="p">(</span><span class="s">"the message goes here"</span><span class="p">)</span>
<span class="nf">bar</span><span class="p">()</span>
</code></pre></div></div>

<p>to dispatch asychronously to the main queue:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">foo</span><span class="p">()</span>
<span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
    <span class="kt">Logging</span><span class="o">.</span><span class="nf">sink</span><span class="p">(</span><span class="s">"the message goes here"</span><span class="p">)</span>
<span class="p">}</span>
<span class="nf">bar</span><span class="p">()</span>
</code></pre></div></div>

<p>Another problem with that change is that it dumps telemetry code (string interpolation, etc.) onto the main queue where it’s not desirable to occur, since it may degrade user interface code execution and introduce scroll hitches.</p>

<p>You might instead propose a more radical change that uses a non-global actor as a shared singleton so that existing synchronous read/write access can be preserved, obscuring the asynchronous details behind the scenes:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Logging</span> <span class="p">{</span>
    <span class="c1">// NOTE: the addition of `@Sendable`</span>
    <span class="kd">typealias</span> <span class="kt">LoggingSink</span> <span class="o">=</span> <span class="kd">@Sendable</span> <span class="p">(</span><span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span>

    <span class="kd">static</span> <span class="k">var</span> <span class="nv">sink</span><span class="p">:</span> <span class="kt">LoggingSink</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span>
            <span class="p">{</span> <span class="n">message</span> <span class="k">in</span> <span class="kt">Task</span> <span class="p">{</span>
                <span class="k">await</span> <span class="n">plumbing</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="n">message</span><span class="p">)</span>
            <span class="p">}</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span>
            <span class="kt">Task</span> <span class="p">{</span>
                <span class="k">await</span> <span class="n">plumbing</span><span class="o">.</span><span class="nf">configure</span><span class="p">(</span><span class="nv">sink</span><span class="p">:</span> <span class="n">newValue</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">plumbing</span> <span class="o">=</span> <span class="kt">Plumbing</span><span class="p">()</span>

    <span class="kd">private</span> <span class="kd">actor</span> <span class="kt">Plumbing</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">sink</span><span class="p">:</span> <span class="kt">LoggingSink</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>

        <span class="kd">func</span> <span class="nf">log</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">sink</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="kd">func</span> <span class="nf">configure</span><span class="p">(</span><span class="nv">sink</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="kt">LoggingSink</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">self</span><span class="o">.</span><span class="n">sink</span> <span class="o">=</span> <span class="n">sink</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This second option compiles without warnings or errors, even with the strictest concurrency checks that are enabled intrinsically when compiling under Swift 6 language mode. It also behaves as expected at runtime. This is a potential option, however it didn’t occur to me until writing this blog post. What occurred to me instead was to find a way to use locking mechanisms to synchronize access to the <code class="language-plaintext highlighter-rouge">static var</code> mutable property. What happened next led me down a path to some code that (A) compiled without warnings or errors but (B) crashed hard at runtime due to implicit actor isolation assertion failures.</p>

<p>It’s this other approach that I want to walk you through, since you might be as tempted as I was to take this path and might be lulled into a false sense of optimism by the lack of compiler warnings.</p>

<p>TL;DR: Seriously, beware the perils of <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code>, it hides more sins than you might guess.</p>

<p>Let’s revisit the original code sample, trimming some fluff for conversational purposes.:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Logging</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">sink</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">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
    <span class="c1">// Error: Static property 'sink' is not concurrency-</span>
    <span class="c1">// safe because it is nonisolated global shared </span>
    <span class="c1">// mutable state</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can resolve the compiler error by making <code class="language-plaintext highlighter-rouge">sink</code> a computed property backed by something that the compiler will accept. Let’s introduce a generic type <code class="language-plaintext highlighter-rouge">Box&lt;T&gt;</code> that is <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code> and can be the backing storage for our property:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Box</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span><span class="p">:</span> <span class="kd">@unchecked</span> <span class="kt">Sendable</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">T</span>
    <span class="nf">init</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="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><em>‌(Side note: I have, for purposes of this blog post, omitted the use of a locking mechanism to synchronize access to the <code class="language-plaintext highlighter-rouge">value</code> property. Such locking ensures that data races are, in practice, not possible. It doesn’t have bearing on the discussion that follows, however, because it amounts to a pinky swear that the Swift compiler cannot verify. It’s really too bad there isn’t a language-level support for this pattern that is concurrency checkable instead of only concurrency ignorable.)</em></p>

<p>Without the <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code>, we’d get a compiler error down below when we try to use a <code class="language-plaintext highlighter-rouge">Box&lt;LoggingSink&gt;</code> to store our property:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Logging</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">LoggingSink</span> <span class="o">=</span> <span class="p">(</span><span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span>

    <span class="kd">static</span> <span class="k">var</span> <span class="nv">sink</span><span class="p">:</span> <span class="kt">LoggingSink</span> <span class="p">{</span>
        <span class="k">get</span> <span class="p">{</span> <span class="n">_sink</span><span class="o">.</span><span class="n">value</span> <span class="p">}</span>
        <span class="k">set</span> <span class="p">{</span> <span class="n">_sink</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">_sink</span> <span class="o">=</span> <span class="kt">Box</span><span class="o">&lt;</span><span class="kt">LoggingSink</span><span class="o">&gt;</span><span class="p">({</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since we have included the <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code> on our <code class="language-plaintext highlighter-rouge">Box</code> type, that ☝🏻 there compiles without warnings or errors. We’ve converted a mutable <code class="language-plaintext highlighter-rouge">static var sink</code> property to a computed property backed by an object that the concurrency checker ignores. This preserves our existing API. It also carries forward all of the existing risks which our consumers have pinky-sworn with us not to get wrong. We could stop here. However, when we wire this code up in a sample project, we encounter a crash at runtime on iOS 18:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@main</span> 
<span class="kd">struct</span> <span class="kt">MyApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="kt">Logging</span><span class="o">.</span><span class="n">sink</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">Scene</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="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="o">...</span> <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">userPressedTheButton</span><span class="p">()</span> <span class="p">{</span>
        <span class="kt">DispatchQueue</span><span class="o">.</span><span class="nf">global</span><span class="p">()</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
            <span class="kt">Logging</span><span class="o">.</span><span class="nf">sink</span><span class="p">(</span><span class="s">"hello"</span><span class="p">)</span> <span class="c1">// &lt;-- CRASH</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here’s the output from the <code class="language-plaintext highlighter-rouge">bt</code> command in lldb:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>* thread #3, queue = 'com.apple.root.default-qos', stop reason = EXC_BREAKPOINT (code=1, subcode=0x101bd43f8)
    frame #0: 0x0000000101bd43f8 libdispatch.dylib`_dispatch_assert_queue_fail + 116
    frame #1: 0x0000000101bd4384 libdispatch.dylib`dispatch_assert_queue + 188
    frame #2: 0x0000000244f8a400 libswift_Concurrency.dylib`swift_task_isCurrentExecutorImpl(swift::SerialExecutorRef) + 284
    frame #3: 0x0000000100838994 FalsePositives.debug.dylib`closure #1 in FalsePositivesApp.init($0="hello") at FalsePositivesApp.swift:0
    frame #4: 0x00000001008385ec FalsePositives.debug.dylib`thunk for @escaping @callee_guaranteed (@guaranteed String) -&gt; () at &lt;compiler-generated&gt;:0
    frame #5: 0x000000010083849c FalsePositives.debug.dylib`thunk for @escaping @callee_guaranteed (@in_guaranteed String) -&gt; (@out ()) at &lt;compiler-generated&gt;:0
  * frame #6: 0x0000000100839850 FalsePositives.debug.dylib`closure #1 in ContentView.userPressedTheButton() at FalsePositivesApp.swift:40:21
    frame #7: 0x00000001008398a0 FalsePositives.debug.dylib`thunk for @escaping @callee_guaranteed @Sendable () -&gt; () at &lt;compiler-generated&gt;:0
    frame #8: 0x0000000101bd0ec0 libdispatch.dylib`_dispatch_call_block_and_release + 24
    frame #9: 0x0000000101bd27b8 libdispatch.dylib`_dispatch_client_callout + 16
    frame #10: 0x0000000101bd55f4 libdispatch.dylib`_dispatch_queue_override_invoke + 1312
    frame #11: 0x0000000101be63d4 libdispatch.dylib`_dispatch_root_queue_drain + 372
    frame #12: 0x0000000101be6f7c libdispatch.dylib`_dispatch_worker_thread2 + 256
    frame #13: 0x000000010099b7d8 libsystem_pthread.dylib`_pthread_wqthread + 224
</code></pre></div></div>

<p>This stack of method calls jumps out at us:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0: _dispatch_assert_queue_fail
1: dispatch_assert_queue
2: swift_task_isCurrentExecutorImpl(swift::SerialExecutorRef)
</code></pre></div></div>

<p>That’s a runtime assertion causing the app to crash. It appears to be asserting that some specific queue managed by a task executor is the expected queue. Since we aren’t using any other actors here except the Main Actor (implicitly the Main Actor since SwiftUI <code class="language-plaintext highlighter-rouge">View</code> and <code class="language-plaintext highlighter-rouge">App</code> protocols are implicitly isolated to the Main Actor), we have to assume that the runtime is doing the equivalent of this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">dispatch_assert_queue</span><span class="p">(</span><span class="o">.</span><span class="n">main</span><span class="p">)</span>
</code></pre></div></div>

<p>But why? Our <code class="language-plaintext highlighter-rouge">LoggingSink</code> function type is implicitly nonisolated:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">typealias</span> <span class="kt">LoggingSink</span> <span class="o">=</span> <span class="p">(</span><span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span>
</code></pre></div></div>

<p>Same goes for the rest of the <code class="language-plaintext highlighter-rouge">Logging</code> namespace and the <code class="language-plaintext highlighter-rouge">Box</code> class. There’s nothing in our logging API surface that would imply Main Actor isolation. Where is that isolation being inferred?</p>

<p>It turns out that the implicit Main Actor isolation is getting introduced by <code class="language-plaintext highlighter-rouge">MyApp</code> where we’ve supplied the LoggingSink:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="kt">Logging</span><span class="o">.</span><span class="n">sink</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">App</code> protocol declaration requires <code class="language-plaintext highlighter-rouge">@MainActor</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="n">macOS</span> <span class="mf">11.0</span><span class="p">,</span> <span class="n">tvOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="n">watchOS</span> <span class="mf">7.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
<span class="kd">@MainActor</span> <span class="kd">@preconcurrency</span> <span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">App</span> <span class="p">{</span>
</code></pre></div></div>

<p>Therefore that <code class="language-plaintext highlighter-rouge">init()</code> method is isolated to the Main Actor. But our <code class="language-plaintext highlighter-rouge">Logging.sink</code> member is <em>not</em> isolated to the Main Actor. It’s implicitly <code class="language-plaintext highlighter-rouge">nonisolated</code>, so why is the compiler inferring Main Actor isolation for the block we pass to it?</p>

<p>I will offer an educated guess about what’s happening here. I believe it’s a combination of three factors:</p>

<h3 id="1it-is-not-possible-to-explicitly-declare-a-function-type-as-nonisolated"><strong>1) It is not possible to explicitly declare a function type as nonisolated.</strong></h3>

<p>You cannot include the <code class="language-plaintext highlighter-rouge">nonisolated</code> keyword as part of the type declaration for a function. We could not, for example, write our app’s initializer like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// Cannot find type 'nonisolated' in scope</span>
        <span class="k">let</span> <span class="nv">sink</span><span class="p">:</span> <span class="nf">nonisolated</span> <span class="p">(</span><span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
        <span class="kt">Logging</span><span class="o">.</span><span class="n">sink</span> <span class="o">=</span> <span class="n">sink</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>This is an important distinction because it has bearing on the next factor that I believe is contributing to the behavior:</p>

<h3 id="2closures-may-always-implicitly-inherit-the-actor-isolation-where-they-are-created"><strong>2) Closures (may? always?) implicitly inherit the actor isolation where they are created.</strong></h3>

<p>If we rewrote our app’s initializer to look like this instead:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@main</span> <span class="kd">struct</span> <span class="kt">FalsePositivesApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">closure</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">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
        <span class="kt">Logging</span><span class="o">.</span><span class="n">sink</span> <span class="o">=</span> <span class="n">closure</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>You might assume that the <code class="language-plaintext highlighter-rouge">closure</code> variable couldn’t possibly be isolated to the Main Actor. It’s right there in the type of the function: <code class="language-plaintext highlighter-rouge">(String) -&gt; Void</code>. According to the language rules as I understand them, function types that are not explicitly isolated to a global actor are implicitly nonisolated. But when we run the app with ☝🏻 that code, we get the same result: a runtime assertion failure and a crash on <code class="language-plaintext highlighter-rouge">dispatch_assert_queue</code>, as if our closure had the type <code class="language-plaintext highlighter-rouge">@MainActor (String) -&gt; Void</code> instead. For some reason here, the Swift 6 compiler is implicitly associating that <code class="language-plaintext highlighter-rouge">{ print($0) }</code> closure with the Main Actor without informing us that it is doing so. I would argue that this is, at least in part, a defect in the compiler. This implicit Main Actor isolation is erroneous. But part of the problem is on us, because we’ve been using <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code>, which causes the compiler to suppress errors that would otherwise bring related problems to the surface. This leads us to the third factor contributing to the observed behavior.</p>

<h3 id="3unchecked-sendable-suppresses-compile-time-concurrency-checks-of-functions-used-as-stored-instance-members-but-does-not-suppress-run-time-concurrency-checks-when-those-functions-are-executed"><strong>3) <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code> suppresses compile-time concurrency checks of functions used as stored instance members, but does not suppress run-time concurrency checks when those functions are executed.</strong></h3>

<p>Phew, that’s a mouthful. Let’s unpack it a bit.</p>

<ul>
  <li><strong>functions used as stored instance members</strong> - This means an instance property like the <code class="language-plaintext highlighter-rouge">var value: T</code> in our <code class="language-plaintext highlighter-rouge">Box</code> type.</li>
  <li><strong>suppresses compile-time checks</strong> - The compiler ignores potential data races in the creation and usage of the stored function at compile time.</li>
  <li><strong>but does not suppress run-time concurrency checks</strong> - Any actor isolation assertions that slip into the code that were suppressed at compile time are not also suppressed at run-time, even if those assertions are erroneous and the code is not actively committing data races.</li>
</ul>

<p>In other words, code that compiles OK may crash at run-time on false positive assertions in code that turns out not to be a legitimate problem.</p>

<p>I do not consider the supression of <strong>compile-time</strong> errors a bug or a defect. Slapping <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code> on something is brazenly constructing a <a href="https://en.wiktionary.org/wiki/footgun">footgun</a>. The Swift 6 compiler is justified in suppressing warnings and errors, and we have been “asking for it” if our app encounters actual data races.</p>

<p>However, I do think it’s impolite for the runtime to enforce isolation assertions on code that has explicitly been asked to suppress such assertions via <code class="language-plaintext highlighter-rouge">@unchecked</code>, especially since they are just naive assertions about the current dispatch queue, not introspection of the actual content of the function being executied. Pinky swears, documentation, peer review, and long stretches of production battle-hardening should be a sufficient counter to any gripes that the Swift 6 runtime may have about our desire to ignore actor isolation. The fact that it is possible for the runtime to not honor my intent here is, I’d argue, if not a defect, at least an annoyance. Let me build my footguns. I promise to only shoot between adjacent toes.</p>

<h3 id="putting-all-three-together">Putting All Three Together</h3>

<p>Let’s see how all three factors come together to create this problem, where compile time seems OK but run-time crashes. Let’s start by, for sake of argument, rewriting our app’s initializer to this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Example</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="kd">func</span> <span class="nf">runThisOnMainActor</span><span class="p">(</span>
        <span class="nv">closure</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">String</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="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
            <span class="c1">// ERROR: Sending 'closure' risks</span>
            <span class="c1">// causing data races:</span>
            <span class="nf">closure</span><span class="p">(</span><span class="s">"example"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">@main</span> <span class="kd">struct</span> <span class="kt">FalsePositivesApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">closure</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">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
        <span class="kt">Example</span><span class="o">.</span><span class="n">runThisOnMainActor</span> <span class="p">{</span> <span class="n">message</span> <span class="k">in</span>
            <span class="nf">closure</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>That will not compile under Swift 6 language mode because we cannot pass <code class="language-plaintext highlighter-rouge">closure</code> into <code class="language-plaintext highlighter-rouge">DispatchQueue.main.async</code> without declaring <code class="language-plaintext highlighter-rouge">@Sending</code> on the <code class="language-plaintext highlighter-rouge">runThisOnMainActor</code> method’s closure argument. The Swift 6 compiler is really good at catching all kinds of flavors of this potential for data races, even if you try stuffing <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code> into the mix:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Example</span><span class="p">:</span> <span class="kd">@unchecked</span> <span class="kt">Sendable</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">runThisOnMainActor</span><span class="p">(</span>
        <span class="nv">closure</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">String</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="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
            <span class="c1">// ERROR: Sending 'closure' risks</span>
            <span class="c1">// causing data races:</span>
            <span class="nf">closure</span><span class="p">(</span><span class="s">"example"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">@main</span> <span class="kd">struct</span> <span class="kt">FalsePositivesApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">closure</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">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
        <span class="kt">Example</span><span class="p">()</span><span class="o">.</span><span class="n">runThisOnMainActor</span> <span class="p">{</span> <span class="n">message</span> <span class="k">in</span>
            <span class="nf">closure</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>But it does have a loophole: functions as stored instance members. If you have a type that is <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code> with a stored instance member that’s a function, then all the compile-time concurrency checking around that function is suppressed. The following code sample compiles without warnings on Swift 6, but crashes at runtime with the assertion failure we’ve been discussing:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Example</span><span class="p">:</span> <span class="kd">@unchecked</span> <span class="kt">Sendable</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">closure</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">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">@main</span> <span class="kd">struct</span> <span class="kt">FalsePositivesApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">closure</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">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
        <span class="k">let</span> <span class="nv">example</span> <span class="o">=</span> <span class="kt">Example</span><span class="p">()</span>
        <span class="n">example</span><span class="o">.</span><span class="n">closure</span> <span class="o">=</span> <span class="n">closure</span>
        <span class="kt">DispatchQueue</span><span class="o">.</span><span class="nf">global</span><span class="p">()</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
            <span class="n">example</span><span class="o">.</span><span class="nf">closure</span><span class="p">(</span><span class="s">"crash!"</span><span class="p">)</span> <span class="c1">// CRASHES</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>What’s so strange here is the nature of our <code class="language-plaintext highlighter-rouge">closure</code> local variable. It’s implicitly nonisolated, but the compiler is baking Main Actor isolation checks into the <code class="language-plaintext highlighter-rouge">{ print($0) }</code> body of the closure. There’s no compile-time warning about any of this because of the loophole in functions as stored instance members of unchecked sendables.</p>

<h3 id="how-to-fix-this">How To Fix This</h3>

<p>Assuming that you just really, really, really want to keep that <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code> in the mix, the way to resolve this issue is to change the function type declaration of the <code class="language-plaintext highlighter-rouge">LoggingSink</code> from:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">typealias</span> <span class="kt">LoggingSink</span> <span class="o">=</span> <span class="p">(</span><span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span>
</code></pre></div></div>

<p>to:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">typealias</span> <span class="kt">LoggingSink</span> <span class="o">=</span> <span class="kd">@Sendable</span> <span class="p">(</span><span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span>
</code></pre></div></div>

<p>With that one change, it no longer becomes possible to set the value of <code class="language-plaintext highlighter-rouge">Logging.sink</code> to anything other than a sendable function:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@main</span> <span class="kd">struct</span> <span class="kt">FalsePositivesApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">closure</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">Void</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
        <span class="c1">// ERROR: Converting non-sendable function </span>
        <span class="c1">// value to '@Sendable (String) -&gt; Void' </span>
        <span class="c1">// may introduce data races:</span>
        <span class="kt">Logging</span><span class="o">.</span><span class="n">sink</span> <span class="o">=</span> <span class="n">closure</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>And the following will both compile without warnings or errors, and will <strong>not</strong> violate an assertion at runtime:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@main</span> <span class="kd">struct</span> <span class="kt">FalsePositivesApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="kt">Logging</span><span class="o">.</span><span class="n">sink</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>Good luck out there.</p>

<h3 id="update-november-13th-2024">Update, November 13th 2024</h3>

<p>Got some helpful responses via Mastodon yesterday.</p>

<p>Matt Massicotte <a href="https://mastodon.social/@mattiem/113471879178243629">writes</a>:</p>

<blockquote>
  <p>What’s happening here is the compiler is reasoning “this closure is not Sendable so it couldn’t possibly change isolation from where it was formed and therefore its body must be MainActor too” but your unchecked type allows this invariant to be violated. This kind of thing comes up a lot in many forms, and it’s hard to debug…</p>
</blockquote>

<p>Both Matt Massicotte and <a href="https://mastodon.social/@cocoaphony/113472300884625933">Rob Napier</a> also brought up <a href="https://developer.apple.com/documentation/synchronization/mutex">the Mutex struct from the Synchronization module</a>, which I keep forgetting about because it has an iOS 18 minimum and is therefore not available to me on my projects. Mutex is analogous to the <code class="language-plaintext highlighter-rouge">Box&lt;T&gt;</code> class pictured in my screenshot above. Let’s look at Mutex’s generated interface alongside the one for <code class="language-plaintext highlighter-rouge">Box&lt;T&gt;</code> to compare and contrast (trimming some boilerplate for clarity).</p>

<p>Here’s Mutex:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Mutex</span><span class="o">&lt;</span><span class="kt">Value</span><span class="o">&gt;</span> <span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span> <span class="k">where</span> <span class="kt">Value</span> <span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="n">withLock</span><span class="o">&lt;</span><span class="kt">Result</span><span class="p">,</span> <span class="kt">E</span><span class="o">&gt;</span><span class="p">(</span>
        <span class="n">_</span> <span class="nv">body</span><span class="p">:</span> <span class="p">(</span><span class="k">inout</span> <span class="kt">Value</span><span class="p">)</span> <span class="nf">throws</span><span class="p">(</span><span class="kt">E</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">sending</span> <span class="kt">Result</span>
    <span class="p">)</span> <span class="nf">throws</span><span class="p">(</span><span class="kt">E</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">sending</span> <span class="kt">Result</span> <span class="k">where</span> <span class="kt">E</span> <span class="p">:</span> <span class="kt">Error</span><span class="p">,</span> <span class="kt">Result</span> <span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">Mutex</span> <span class="p">:</span> <span class="kd">@unchecked</span> <span class="kt">Sendable</span> <span class="k">where</span> <span class="kt">Value</span> <span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span> <span class="p">{</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And here’s Box:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Box</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span><span class="p">:</span> <span class="kd">@unchecked</span> <span class="kt">Sendable</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="n">access</span><span class="o">&lt;</span><span class="kt">Output</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">block</span><span class="p">:</span> <span class="p">(</span><span class="k">inout</span> <span class="kt">T</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Output</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Output</span>
<span class="p">}</span>
</code></pre></div></div>

<p>First, note how both of them require <code class="language-plaintext highlighter-rouge">@unchecked</code> in order to conform to <code class="language-plaintext highlighter-rouge">Sendable</code>. This is because there is no <strong>language-level</strong> primitive or concurrency structure that permit synchronized <strong>synchronous</strong> access to be checked for correctness by the concurrency checker. At best we can only suppress false positives. It sure would be nice if there was a language-level way to enforce correctness, though I suppose that will matter less in the future once apps can start declaring an iOS 18 minimum and rely on Mutex for these needs.</p>

<p>But prior to iOS 18, one has to roll their own alternative to Mutex in order to implement synchronous access to a synchronized resource, which is what my <code class="language-plaintext highlighter-rouge">Box&lt;T&gt;</code> example class is attempting to do. But note one key difference: Box is generic over anything. It’s just <code class="language-plaintext highlighter-rouge">T</code>. But Mutex enforces an additional constraint: <code class="language-plaintext highlighter-rouge">~Copyable</code>. This means “not Copyable”, and it has unique behavior among protocols in Swift. I recommending reading (and rereading multiple times until you can comprehend it) <a href="https://developer.apple.com/documentation/Swift/Copyable">the Copyable documentation</a>. There are some truly astounding bits of information in there:</p>

<h3 id="astounding-bit-1-all-generic-type-parameters-implicitly-include-copyable-in-their-list-of-requirements">Astounding Bit #1) All generic type parameters implicitly include <code class="language-plaintext highlighter-rouge">Copyable</code> in their list of requirements.</h3>

<p>If your codebase has a generic type, it’s using <code class="language-plaintext highlighter-rouge">Copyable</code> already. That means that my <code class="language-plaintext highlighter-rouge">Box&lt;T&gt;</code> type, despite what I said above, <em>does</em> have an implicit constraint:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Box</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Copyable</span><span class="o">&gt;</span>
</code></pre></div></div>

<p>All classes and actors implicitly conform to Copyable, and it’s not possible to declare one that doesn’t. All structs and enums implicitly conform to Copyable, but it is possible to state definitely that one does not. Check this out:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Box</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">value</span><span class="p">:</span> <span class="kt">T</span>
    <span class="nf">init</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="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">AintCopyable</span><span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">32</span>
<span class="p">}</span>

<span class="k">let</span> <span class="nv">box</span> <span class="o">=</span> <span class="kt">Box</span><span class="p">(</span><span class="kt">AintCopyable</span><span class="p">())</span>
<span class="c1">// ERROR: Generic class 'Box' requires that </span>
<span class="c1">// 'AintCopyable' conform to 'Copyable'</span>
</code></pre></div></div>

<h3 id="astounding-bit-2-both-copyable-and-noncopyable-types-can-conform-to-protocols-or-generic-requirements-that-inherit-from-copyable">Astounding Bit #2) Both copyable and noncopyable types can conform to protocols or generic requirements that inherit from <code class="language-plaintext highlighter-rouge">~Copyable</code>.</h3>

<p>This is counterintuitive to say the least. You would think “not copyable” would exclude, as a logical categorization, anything that is “copyable”. But you’d be wrong, at least when it comes to generics and protocol requirements. Here’s an example based on the one given in the Copyable documentation:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">HeadScratching</span><span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">foo</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span> <span class="k">get</span> <span class="k">set</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">IsCopyable</span><span class="p">:</span> <span class="kt">Copyable</span><span class="p">,</span> <span class="kt">HeadScratching</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">32</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">AintCopyable</span><span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span><span class="p">,</span> <span class="kt">HeadScratching</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">32</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That ☝🏻 there compiles without warnings or errors. This next example <em>almost</em> compiles, except for some concurrency errors I will explain in a moment, the explanation of which will bring us to another Astounding Bit about the Copyable protocol:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Box</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">T</span>
    <span class="nf">init</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="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">AintCopyable</span><span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">32</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">IsCopyable</span><span class="p">:</span> <span class="kt">Copyable</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">foo</span> <span class="o">=</span> <span class="mi">32</span>
<span class="p">}</span>

<span class="c1">// Allowed:</span>
<span class="k">let</span> <span class="nv">boxA</span> <span class="o">=</span> <span class="kt">Box</span><span class="p">(</span><span class="kt">AintCopyable</span><span class="p">())</span>

<span class="c1">// Also allowed:</span>
<span class="k">let</span> <span class="nv">boxB</span> <span class="o">=</span> <span class="kt">Box</span><span class="p">(</span><span class="kt">IsCopyable</span><span class="p">())</span>
</code></pre></div></div>

<h3 id="astounding-bit-3-non-copyable-values-cannot-be-parameterized-without-specifying-their-ownership">Astounding Bit #3) Non-copyable values cannot be parameterized without specifying their ownership.</h3>

<p>To put it another way, look at this compiler error from my preceding example:</p>

<p class="inline-image"><a href="https://jaredsinclair.com/assets/img/false-positives-noncopyable.png"><img src="https://jaredsinclair.com/assets/img/false-positives-noncopyable.png" /></a></p>

<p>You must specify one of the following ownership options for the <code class="language-plaintext highlighter-rouge">value</code> parameter since it is required to be non-copyable. The <code class="language-plaintext highlighter-rouge">borrowing</code> option is not useful for our purposes here because we’re trying to construct a threadsafe mutable reference to a shared value, and <code class="language-plaintext highlighter-rouge">borrowing</code> would not allow us to store <code class="language-plaintext highlighter-rouge">value</code> in a property. The <code class="language-plaintext highlighter-rouge">inout</code> option is slightly more usable, because it allows us to store <code class="language-plaintext highlighter-rouge">value</code> in a property, however it permits both Box and the caller using a Box to have read/write access to the same piece of data. That’s not going to fly when we’re trying to prevent data races. That leaves only the <code class="language-plaintext highlighter-rouge">consuming</code> option, which is what you see in <a href="https://github.com/swiftlang/swift/blob/53f772cd434f931d9edfe92943aba986705868db/stdlib/public/Synchronization/Mutex/Mutex.swift#L50-L52">Mutex’s initializer</a>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Mutex</span><span class="o">&lt;</span><span class="kt">Value</span><span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">initialValue</span><span class="p">:</span> <span class="n">consuming</span> <span class="n">sending</span> <span class="kt">Value</span><span class="p">)</span> <span class="p">{</span>
        <span class="o">...</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>This all bears repeating: the compiler forces you to select an explicit memory ownership for any generic parameter that is noncopyable (<code class="language-plaintext highlighter-rouge">\~Copyable</code>). This makes <code class="language-plaintext highlighter-rouge">\~Copyable</code> especially useful for use with data synchronization utilities like Mutex. We want to be as explicit as possible, at compile time, about where and how data is permitted to mutate.</p>

<h3 id="astounding-bit-4-non-copyable-parameters-with-consuming-ownership-help-diagnose-concurrency-issues-at-compile-time">Astounding Bit #4) Non-copyable parameters with <code class="language-plaintext highlighter-rouge">consuming</code> ownership help diagnose concurrency issues at compile time.</h3>

<p>Once you’ve selected the <code class="language-plaintext highlighter-rouge">consuming</code> ownership, there are some useful secondary effects that cascade from that choice. Let’s look at a contrived example to learn more:</p>

<p class="inline-image"><a href="https://jaredsinclair.com/assets/img/false-positives-nonsendable.png"><img src="https://jaredsinclair.com/assets/img/false-positives-nonsendable.png" /></a></p>

<p>The Swift 6 compiler is able to deduce that we’ve introduced a potential data race. The <code class="language-plaintext highlighter-rouge">box</code> declaration is at a global scope, and contains mutable state. The fixits in that screenshot list several options. Let’s use <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code> and add our own locking mechanism to prevent a data race:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Box</span><span class="o">&lt;</span><span class="kt">T</span><span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span><span class="o">&gt;</span><span class="p">:</span> <span class="kd">@unchecked</span> <span class="kt">Sendable</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">_value</span><span class="p">:</span> <span class="kt">T</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">lock</span> <span class="o">=</span> <span class="kt">NSLock</span><span class="p">()</span>

    <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="n">consuming</span> <span class="kt">T</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">_value</span> <span class="o">=</span> <span class="n">value</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="n">withLock</span><span class="o">&lt;</span><span class="kt">Output</span><span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span><span class="o">&gt;</span><span class="p">(</span>
        <span class="n">_</span> <span class="nv">body</span><span class="p">:</span> <span class="p">(</span><span class="k">inout</span> <span class="kt">T</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Output</span>
    <span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Output</span> <span class="p">{</span>
        <span class="n">lock</span><span class="o">.</span><span class="nf">lock</span><span class="p">()</span>
        <span class="k">defer</span> <span class="p">{</span> <span class="n">lock</span><span class="o">.</span><span class="nf">unlock</span><span class="p">()</span> <span class="p">}</span>
        <span class="k">return</span> <span class="nf">body</span><span class="p">(</span><span class="o">&amp;</span><span class="n">_value</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">let</span> <span class="nv">box</span> <span class="o">=</span> <span class="kt">Box</span><span class="p">(</span><span class="kc">false</span><span class="p">)</span>
</code></pre></div></div>

<p>That all compiles now, without warnings or errors. We’ve still got a <code class="language-plaintext highlighter-rouge">var value: T</code> property, but it’s a computed property that synchronizes access via the <code class="language-plaintext highlighter-rouge">withLock</code> method, which uses an NSLock to synchronize concurrent reads and writes. But this example is still just using a Bool, which is pretty contrived. What happens if we go back to my original example above of a <code class="language-plaintext highlighter-rouge">(String)-&gt; Void</code> function type instead of a <code class="language-plaintext highlighter-rouge">Bool</code>?</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">sharedBox</span> <span class="o">=</span> <span class="kt">Box</span><span class="o">&lt;</span><span class="p">(</span><span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="o">&gt;</span><span class="p">({</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="p">})</span>

<span class="kd">@main</span> <span class="kd">struct</span> <span class="kt">FalsePositivesApp</span><span class="p">:</span> <span class="kt">App</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">sharedBox</span><span class="o">.</span><span class="n">withLock</span> <span class="p">{</span>
            <span class="nv">$0</span> <span class="o">=</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"custom: </span><span class="se">\(</span><span class="nv">$0</span><span class="se">)</span><span class="s">"</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">Scene</span> <span class="p">{</span> <span class="kt">WindowGroup</span> <span class="p">{</span> <span class="kt">ContentView</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">Button</span><span class="p">(</span><span class="s">"Press Me."</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">userPressedTheButton</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">userPressedTheButton</span><span class="p">()</span> <span class="p">{</span>
        <span class="kt">DispatchQueue</span><span class="o">.</span><span class="nf">global</span><span class="p">()</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">block</span> <span class="o">=</span> <span class="n">sharedBox</span><span class="o">.</span><span class="n">withLock</span> <span class="p">{</span> <span class="nv">$0</span> <span class="p">}</span>
            <span class="nf">block</span><span class="p">(</span><span class="s">"print me"</span><span class="p">)</span> <span class="c1">// &lt;--- CRASHES</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It compiles without errors, but it crashes at runtime with the same main queue assertion we saw earlier in this blog post. What gives? Well, we’ve omitted something from our definition of the <code class="language-plaintext highlighter-rouge">withLock</code> method that is seen in Mutex’s equivalent method: a bunch of <code class="language-plaintext highlighter-rouge">sending</code> keywords. This keyword was <a href="https://github.com/swiftlang/swift-evolution/blob/main/proposals/0430-transferring-parameters-and-results.md">introduced in SE-0430</a>. It’s a bit wonkish to explain, but it allows a function parameter and/or result to indicate to the compiler that it might be sent over a concurrency isolation boundary (like, say, from the main actor to some other region). It’s akin to <code class="language-plaintext highlighter-rouge">@Sendable</code>, but less dramatic. It patches some holes in the concurrency checker that otherwise made it difficult to do things like: prevent concurrent access to a non-Sendable object after it got passed as a parameter into an actor’s initializer. The proposal is worth reading carefully, I won’t summarize it more than that now.</p>

<p>Here’s our Box’s <code class="language-plaintext highlighter-rouge">withLock</code> method with those <code class="language-plaintext highlighter-rouge">sending</code> keywords added:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">withLock</span><span class="o">&lt;</span><span class="kt">Output</span><span class="p">:</span> <span class="o">~</span><span class="kt">Copyable</span><span class="o">&gt;</span><span class="p">(</span>
    <span class="n">_</span> <span class="nv">body</span><span class="p">:</span> <span class="p">(</span><span class="k">inout</span> <span class="n">sending</span> <span class="kt">T</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">sending</span> <span class="kt">Output</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">sending</span> <span class="kt">Output</span>
</code></pre></div></div>

<p>What happens if we try to compile now? Check it out:</p>

<p class="inline-image"><a href="https://jaredsinclair.com/assets/img/false-positives-end-of-function.png"><img src="https://jaredsinclair.com/assets/img/false-positives-end-of-function.png" /></a></p>

<p>The text of those errors is underwhelming, but their presence is helpful. It’s detected that we’re sending a non-Sendable function type either from the Main Actor into some unspecified region (“…cannot be main actor-isolated at…”), or from one unspecified region into another unspecified region (“…cannot be task-isolated at…”). The solution is what we discovered earlier: to require our LoggingSink function type to be <code class="language-plaintext highlighter-rouge">@Sendable</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">typealias</span> <span class="kt">LoggingSink</span> <span class="o">=</span> <span class="kd">@Sendable</span> <span class="p">(</span><span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span>
</code></pre></div></div>

<p>With that one change, the compiler errors go away and the runtime crashes are resolved, too.</p>

<h3 id="ok-but-why-are-you-blathering-on-about-this-didnt-we-already-figure-that-out-earlier">OK, But why are you blathering on about this? Didn’t we already figure that out earlier?</h3>

<p>Here’s why I think this matters. There’s one crucial difference between yesterday’s implementation of <code class="language-plaintext highlighter-rouge">Box&lt;T&gt;</code> and today’s implementation <code class="language-plaintext highlighter-rouge">Box&lt;T: ~Copyable&gt;</code>: the noncopyable version detected, at compile time, that we were sending a non-Sendable function type across isolation regions in a way that would trigger runtime actor isolation checks. The version that is implicitly copyable does <em>not</em> expose this information to the concurrency checker at compile time. This is true even though <strong>both implementations of Box use <code class="language-plaintext highlighter-rouge">@unchecked Sendable</code></strong>. The internals of Box are still unchecked by the concurrency checker, but the values passed into and out of the Box are better checked when <code class="language-plaintext highlighter-rouge">~Copyable</code> and <code class="language-plaintext highlighter-rouge">consuming</code> and <code class="language-plaintext highlighter-rouge">sending</code> are in the mix.</p>

<p>So if, like me, you are unable to use Mutex yet because it requires iOS 18 or later and, like me, you are on the hook to write a replacement that supports earlier OS versions, I think you and I both should consider mimicking the design of Mutex, in particular its compile time checks via <code class="language-plaintext highlighter-rouge">~Copyable</code>, <code class="language-plaintext highlighter-rouge">consuming</code>, and <code class="language-plaintext highlighter-rouge">sending</code>.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[I ran into some unexpected runtime crashes recently while testing an app on iOS 18 compiled under Swift 6 language mode, and the root causes ended up being the perils of using @unchecked Sendable in combination with some counterintuitive compiler behavior with implicit actor isolation. Rather than start at the end, I’ll walk you through how I introduced the crash, and then what I did to resolve it.]]></summary></entry><entry><title type="html">How Do You Know Whether or Not SwiftUI Previews and Preview Content Are Excluded From App Store Builds?</title><link href="https://jaredsinclair.com/2024/05/20/preview-content.html" rel="alternate" type="text/html" title="How Do You Know Whether or Not SwiftUI Previews and Preview Content Are Excluded From App Store Builds?" /><published>2024-05-20T11:00:00+00:00</published><updated>2024-05-20T11:00:00+00:00</updated><id>https://jaredsinclair.com/2024/05/20/preview-content</id><content type="html" xml:base="https://jaredsinclair.com/2024/05/20/preview-content.html"><![CDATA[<p>I’ve found what I believe to be a bug, or at least deeply disappointing behavior, in Xcode’s treatment of SwiftUI previews. I’ll put an explanation together in the paragraphs that follow, but the <strong>TL;DR</strong> is: I think you’ll probably want to start wrapping all your SwiftUI Previews and Preview Content Swift source code in <code class="language-plaintext highlighter-rouge">#if DEBUG</code> active compilation condition checks.</p>

<p class="inline-image"><a href="https://jaredsinclair.com/assets/img/preview-build-failure.png"><img src="https://jaredsinclair.com/assets/img/preview-build-failure.png" /></a><br /><small>Screenshot of an Archive action build failure.</small></p>

<p>SwiftUI previews are, if you consider it from a broader perspective, comprised of two kinds of source code:</p>

<ul>
  <li>
    <p><strong>Preview proper:</strong> - The actual <code class="language-plaintext highlighter-rouge">#Preview</code> or <code class="language-plaintext highlighter-rouge">PreviewProvider</code> source code, typically at the bottom of the participating SwiftUI file.</p>
  </li>
  <li>
    <p><strong>Development Assets:</strong> - Ancillary source (and assets) under a particular directory whose contents should only be used to populate SwiftUI previews. The default name for this directory (in <code class="language-plaintext highlighter-rouge">File/New...</code>) projects is <code class="language-plaintext highlighter-rouge">Preview Content</code>.</p>
  </li>
</ul>

<p>The SwiftUI preview dream is supposed to be this:</p>

<ul>
  <li>
    <p>Dream Part 1: I write <code class="language-plaintext highlighter-rouge">#Preview { ... }</code> or a <code class="language-plaintext highlighter-rouge">PreviewProvider</code> at the bottom of my file.</p>
  </li>
  <li>
    <p>Dream Part 2: I stash any images, assets, or source code (representative data) in the Preview Content directory of Development Assets.</p>
  </li>
  <li>
    <p>Dream Part 3: Xcode ensures that both my Preview proper, and all my Development Assets, are omitted from App Store builds.</p>
  </li>
</ul>

<p>The reality is less than that ideal:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">#Preview</code> and <code class="language-plaintext highlighter-rouge">PreviewProvider</code> source code is stripped out of any build where Swift Optimizations are anything above <code class="language-plaintext highlighter-rouge">-Onone</code>. It (hilariously) does not matter whether or not you’ve got the <code class="language-plaintext highlighter-rouge">ENABLE_PREVIEWS</code> build setting set to <code class="language-plaintext highlighter-rouge">No</code>, or whether your build configuration is named <code class="language-plaintext highlighter-rouge">Release</code> or not. They are included in any unoptimized build.</p>
  </li>
  <li>
    <p>Once more, with feeling: Preview source code is included in any unoptimized build.</p>
  </li>
  <li>
    <p>Note I said, two bullets ago, “stripped out”. Previews are actually built in <strong>all</strong> builds, but they are later stripped out of the compiled binary as part of code optimization. They aren’t skipped altogether, not even for optimized builds. This will have bearing on the bullet points that follow. . . .</p>
  </li>
  <li>
    <p>Development Assets are <strong>not</strong> always built. They are omitted at the front end of the build process during Archiving <strong>only</strong> (i.e. running “Archive” from the Xcode GUI menu, or the <code class="language-plaintext highlighter-rouge">archive</code> <em>xcodebuild</em> command), and even then only if you’ve defined the <code class="language-plaintext highlighter-rouge">DEVELOPMENT_ASSET_PATHS</code> build setting. Any images, asset catalogs, or source files under those paths are excluded from build input. If you aren’t using “Archive”/<code class="language-plaintext highlighter-rouge">archive</code>, you won’t see a build error!</p>
  </li>
  <li>
    <p>This means that if you’ve got a SwiftUI Preview that depends upon <code class="language-plaintext highlighter-rouge">.swift</code> source code, any “Archive”/<code class="language-plaintext highlighter-rouge">archive</code> attempt will fail at build time because the Preview depends upon source code that no longer exists among the build inputs.</p>
  </li>
</ul>

<p>There are several workarounds, none of which are spectacular:</p>

<ul>
  <li>
    <p><strong>Stop using SwiftUI previews.</strong> - Boo, a non-starter.</p>
  </li>
  <li>
    <p><strong>Leave Development Assets hanging around in optimized builds.</strong> - This is done by removing the definition of the <code class="language-plaintext highlighter-rouge">DEVELOPMENT_ASSET_PATHS</code> build setting. Also “Boo” because the whole point of Development Assets is that you don’t want them showing up in App Store builds.</p>
  </li>
  <li>
    <p><strong>Wrap all SwiftUI previews in <code class="language-plaintext highlighter-rouge">#if DEBUG</code> active compilation condition checks.</strong> - Unless you’ve overridden new project defaults, Release build configurations do not define <code class="language-plaintext highlighter-rouge">DEBUG</code> as a Swift active compilation condition. Wrapping all your SwiftUI Previews in <code class="language-plaintext highlighter-rouge">#if DEBUG</code> is a semi-bulletproof solution that will ensure your Previews don’t break optimized builds. But it doesn’t, on its own, prevent development assets from creeping into App Store builds because you might have forgotten to define the <code class="language-plaintext highlighter-rouge">DEVELOPMENT_ASSET_PATHS</code> build setting. If the build setting is missing, your development assets are potentially delivered in App Store builds. I say “potentially” because such a build is, after all, optimized, and therefore if no production code is actually accessing the development asset source code, it’s possible it may be optimized out of the final binary.</p>
  </li>
  <li>
    <p><strong>Wrap all SwiftUI previews AND Development Assets Swift source in <code class="language-plaintext highlighter-rouge">#if DEBUG</code> active compilation condition checks.</strong> - This is the only solution, short of improvements to Xcode itself, that ensures that Previews don’t break optimized builds and that Development Assets source code doesn’t leak into App Store builds.</p>
  </li>
</ul>

<p>That’s the long-winded explanation.</p>

<p>Until and unless Apple makes ergonomic improvements to align SwiftUI Preview and Development Asset conditional compilation techniques, I recommend wrapping both kinds of source code in <code class="language-plaintext highlighter-rouge">#if DEBUG</code> to prevent accidental slippage of test data and source into production code, as well as to prevent unexpected build failures on multitenant hardware (for teams that build for App Store in CI, these build failures often don’t appear except on unattended machines around the time a release is being cut, making them perniciously difficult to spot during day-to-day code review).</p>]]></content><author><name></name></author><summary type="html"><![CDATA[I’ve found what I believe to be a bug, or at least deeply disappointing behavior, in Xcode’s treatment of SwiftUI previews. I’ll put an explanation together in the paragraphs that follow, but the TL;DR is: I think you’ll probably want to start wrapping all your SwiftUI Previews and Preview Content Swift source code in #if DEBUG active compilation condition checks.]]></summary></entry><entry><title type="html">Be Careful When You Initialize a State Object</title><link href="https://jaredsinclair.com/2024/03/14/state-object-autoclosure.html" rel="alternate" type="text/html" title="Be Careful When You Initialize a State Object" /><published>2024-03-14T11:00:00+00:00</published><updated>2024-03-14T11:00:00+00:00</updated><id>https://jaredsinclair.com/2024/03/14/state-object-autoclosure</id><content type="html" xml:base="https://jaredsinclair.com/2024/03/14/state-object-autoclosure.html"><![CDATA[<p>I’m going to share some best practices when using <code class="language-plaintext highlighter-rouge">@StateObject</code> property wrappers, things learned the hard way, via some bugs that were difficult to diagnose and nearly impossible to notice during code review—unless one knows what to look for.</p>

<p>The short version is this: if you have to explicitly initialize a <code class="language-plaintext highlighter-rouge">@StateObject</code>, pay close attention to the fact that the property wrapper’s initialization parameter is an escaping closure called <code class="language-plaintext highlighter-rouge">thunk</code>, not an object called <code class="language-plaintext highlighter-rouge">wrappedValue</code>. Do all the wrapped object initialization and prep inside the closure, or else you’ll undermine the performance benefits that likely motivated you to use <code class="language-plaintext highlighter-rouge">@StateObject</code> in the first place.</p>

<p>Several years ago, before the <code class="language-plaintext highlighter-rouge">@StateObject</code> property wrapper was introduced, if your SwiftUI view needed to create and own an object to perform view-specific duties that can only be performed by a reference type (say, to coordinate some Combine publishers), the only option was an <code class="language-plaintext highlighter-rouge">@ObservedObject</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@ObservedObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">someObject</span> <span class="o">=</span> <span class="kt">SomeObject</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>A chief problem with this API is that the wrapped object’s initializer (in this example the <code class="language-plaintext highlighter-rouge">= SomeObject()</code>) would be run every time <code class="language-plaintext highlighter-rouge">MyView</code>, the struct, was initialized. Since this view is just a child of some other ancestor view, any time the ancestor’s <code class="language-plaintext highlighter-rouge">body</code> property gets accessed, <code class="language-plaintext highlighter-rouge">MyView</code> will be initialized anew, causing <code class="language-plaintext highlighter-rouge">SomeObject()</code> to be initialized again and again:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">SomeAncestor</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">MyView</span><span class="p">()</span> <span class="o">&lt;--</span> <span class="n">gets</span> <span class="n">invoked</span> <span class="n">anytime</span> <span class="kt">SomeAncestor</span><span class="o">.</span><span class="n">body</span> <span class="k">is</span> <span class="n">read</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Remember that a SwiftUI <code class="language-plaintext highlighter-rouge">View</code> is not the view object you see on screen, but rather just a template describing the view object that will be created for you at a later time. Since the <code class="language-plaintext highlighter-rouge">body</code> property of a view returns merely a template, the guts of the SwiftUI framework operate under the assumption that a <code class="language-plaintext highlighter-rouge">body</code> can be accessed as many times as needed to recompute these templates.</p>

<p>To prevent unwanted successive initialization of wrapped objects, the <code class="language-plaintext highlighter-rouge">@StateObject</code> property wrapper was introduced. It is often a simple drop-in replacement for an <code class="language-plaintext highlighter-rouge">@ObservedObject</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@StateObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">someObject</span> <span class="o">=</span> <span class="kt">SomeObject</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this change, anytime SwiftUI traverses the view hierarchy, recursively calling into <code class="language-plaintext highlighter-rouge">body</code> property after <code class="language-plaintext highlighter-rouge">body</code> property, if possible, the storage mechanism within the <code class="language-plaintext highlighter-rouge">@StateObject</code> property wrapper will be carried forward to the new view struct without causing the wrapped object to be initialized again. It’s a bit magical, but honestly a bit too magical, since what I’ve just described contains two hidden details that need close attention.</p>

<p>First, when I wrote “…if possible…” in the previous paragraph, I was referring to the fact that SwiftUI needs to be able to make the determination that two instances of a given view struct should be interpreted as being templates for the exact same on-screen view object. The term for this concept is “identity”. Two <code class="language-plaintext highlighter-rouge">View</code> structs are understood to have the same identity if they share the same identifier. This identifier can be either explicit, or implied.</p>

<p>Explicit identification looks like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">SomeAncestor</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">MyView</span><span class="p">()</span>
            <span class="o">.</span><span class="nf">id</span><span class="p">(</span><span class="s">"the-one-true-view"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Implicit identification is harder to grok. Sometimes it can be inferred from the combination of an <code class="language-plaintext highlighter-rouge">Identifiable</code> model in conjunction with a <code class="language-plaintext highlighter-rouge">ForEach</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Thing</span><span class="p">:</span> <span class="kt">Identifiable</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">String</span> <span class="o">&lt;---</span> <span class="kd">required</span>
    <span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">SomeAncestor</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">stuff</span><span class="p">:</span> <span class="p">[</span><span class="kt">Thing</span><span class="p">]</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">ForEach</span><span class="p">(</span><span class="n">stuff</span><span class="p">)</span> <span class="p">{</span> <span class="n">thing</span> <span class="k">in</span>
            <span class="kt">MyView</span><span class="p">(</span><span class="n">thing</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the above example, the particular <code class="language-plaintext highlighter-rouge">init</code> method of the <code class="language-plaintext highlighter-rouge">ForEach</code> accepts a collection of <code class="language-plaintext highlighter-rouge">Identifiable</code> model values, which allows the guts of the <code class="language-plaintext highlighter-rouge">ForEach</code> body to assign identifiers to each <code class="language-plaintext highlighter-rouge">MyView</code>, automatically on your behalf, using the <code class="language-plaintext highlighter-rouge">id</code> properties of the model values. Here’s that initializer from SwiftUI’s public interface:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">ForEach</span> 
<span class="k">where</span> <span class="kt">ID</span> <span class="o">==</span> <span class="kt">Data</span><span class="o">.</span><span class="kt">Element</span><span class="o">.</span><span class="kt">ID</span><span class="p">,</span> 
<span class="kt">Content</span> <span class="p">:</span> <span class="kt">AccessibilityRotorContent</span><span class="p">,</span>
 <span class="kt">Data</span><span class="o">.</span><span class="kt">Element</span> <span class="p">:</span> <span class="kt">Identifiable</span> 
 <span class="p">{</span>
    <span class="nf">init</span><span class="p">(</span>
        <span class="n">_</span> <span class="nv">data</span><span class="p">:</span> <span class="kt">Data</span><span class="p">,</span> 
        <span class="kd">@AccessibilityRotorContentBuilder</span> <span class="nv">content</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Data</span><span class="o">.</span><span class="kt">Element</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Content</span>
    <span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>SwiftUI has other mechanisms to try to infer identity, but if you’re not explicitly providing identity for a view that owns a <code class="language-plaintext highlighter-rouge">@StateObject</code>, it’s possible that your wrapped object is getting intialized more often than you desire. Setting breakpoints at smart places (like the <code class="language-plaintext highlighter-rouge">init()</code> method of your wrapped object) is a helpful place to look.</p>

<p>I wrote that there were two hidden details hiding in the magic of <code class="language-plaintext highlighter-rouge">@StateObject</code>. I just described the first one, it’s hidden reliance on view identity, but there is another issue that’s particularly subtle, and that’s the mechanism by which it’s possible for a <code class="language-plaintext highlighter-rouge">@StateObject</code> to avoid duplicate initializations of its wrapped object. The best way to see it is by looking at the public interface:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@propertyWrapper</span> 
<span class="kd">struct</span> <span class="kt">StateObject</span><span class="o">&lt;</span><span class="kt">ObjectType</span><span class="o">&gt;</span> <span class="p">:</span> <span class="kt">DynamicProperty</span> 
<span class="k">where</span> <span class="kt">ObjectType</span> <span class="p">:</span> <span class="kt">ObservableObject</span> 
<span class="p">{</span>
    <span class="nf">init</span><span class="p">(</span><span class="n">wrappedValue</span> <span class="nv">thunk</span><span class="p">:</span> <span class="kd">@autoclosure</span> <span class="kd">@escaping</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">ObjectType</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Look closely: the initialization parameter is an escaping closure called <code class="language-plaintext highlighter-rouge">thunk</code>, marked with the <code class="language-plaintext highlighter-rouge">@autoclosure</code> label. The parameter is <strong>not</strong> the object type itself. You might have assumed, like I did at first, that the initalizer looked like this:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@propertyWrapper</span> 
<span class="kd">struct</span> <span class="kt">StateObject</span><span class="o">&lt;</span><span class="kt">ObjectType</span><span class="o">&gt;</span> <span class="p">:</span> <span class="kt">DynamicProperty</span> 
<span class="k">where</span> <span class="kt">ObjectType</span> <span class="p">:</span> <span class="kt">ObservableObject</span> 
<span class="p">{</span>
    <span class="nf">init</span><span class="p">(</span><span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">ObjectType</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This might seem like an academic distinction, until you run into a situation where your view needs to explicitly initialize the <code class="language-plaintext highlighter-rouge">@StateObject</code> property wrapper. If you aren’t careful, it’s possible to completely undermine the benefits of StateObject.</p>

<p>Consider this example, which is similar to something that I actually had to do in some production code. Let’s say that I have <a href="https://jaredsinclair.com/sodes/">a podcast app</a> with a view that displays an episode’s download progress:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">DownloadProgressView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>My app uses CoreData, so I have a class <code class="language-plaintext highlighter-rouge">Episode</code> that’s a managed object. It has some properties, among others, that track download progress for that episode:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Episode</span><span class="p">:</span> <span class="kt">NSManagedObject</span> <span class="p">{</span>
    <span class="kd">@NSManaged</span> <span class="k">var</span> <span class="nv">bytesDownloaded</span><span class="p">:</span> <span class="kt">Int</span>
    <span class="kd">@NSManaged</span> <span class="k">var</span> <span class="nv">bytesExpected</span><span class="p">:</span> <span class="kt">Int</span>
    <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I need my view to update in response to changes in those properties (and let’s say, for reasons outside the scope of this post, it isn’t possible to migrate this app to SwiftData, because it’s not ready for the limelight), which means I need to use KVO to observe the <code class="language-plaintext highlighter-rouge">bytesDownloaded</code> and <code class="language-plaintext highlighter-rouge">bytesExpected</code> properties. Since I can’t do that observation from my <code class="language-plaintext highlighter-rouge">DownloadProgressView</code> directly, I’ll need an intermediate object that sits between the managed object and the view:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">DownloadProgressObserver</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="kd">@Published</span> <span class="kd">private(set)</span> <span class="k">var</span> <span class="nv">progress</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="nf">init</span><span class="p">(</span><span class="nv">episode</span><span class="p">:</span> <span class="kt">Episode</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
        <span class="nf">startObserving</span><span class="p">(</span><span class="n">episode</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The only thing left is to update my view to use this new class. Since nothing else in my app needs this intermediate object, it’s sensible for my view itself to be what creates and owns it, just for the lifetime of my view being on screen. Sounds like a <code class="language-plaintext highlighter-rouge">@StateObject</code> is a good fit:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">DownloadProgressView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@StateObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">observer</span> <span class="o">=</span> <span class="kt">DownloadProgressObserver</span><span class="p">(</span><span class="nv">episode</span><span class="p">:</span> <span class="kt">WHAT_GOES_HERE</span><span class="p">)</span>
    <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>OK, so I cannot use a default value to populate the observer, because my observer has a required initialization argument that cannot be obtained until runtime. So I need to provide an explicit initializer for my view:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">DownloadProgressView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@StateObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">DownloadProgressObserver</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">episode</span><span class="p">:</span> <span class="kt">Episode</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">observer</span> <span class="o">=</span> <span class="kt">DownloadProgressObserver</span><span class="p">(</span><span class="nv">episode</span><span class="p">:</span> <span class="n">episode</span><span class="p">)</span>
        <span class="n">_observer</span> <span class="o">=</span> <span class="kt">StateObject</span><span class="p">(</span><span class="nv">wrappedValue</span><span class="p">:</span> <span class="n">observer</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Looks great, right? Actually, it’s really bad. Because I initialize the observer object as a separate statement, and pass a local variable as the <code class="language-plaintext highlighter-rouge">wrappedValue</code>, it is (sort-of, in a pseudocode-y way) equivalent to the following code:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">observer</span> <span class="o">=</span> <span class="kt">DownloadProgressObserver</span><span class="p">(</span><span class="nv">episode</span><span class="p">:</span> <span class="n">episode</span><span class="p">)</span>
<span class="n">_observer</span> <span class="o">=</span> <span class="kt">StateObject</span><span class="p">(</span><span class="nv">thunk</span><span class="p">:</span> <span class="p">{</span> <span class="n">observer</span> <span class="p">})</span>
</code></pre></div></div>

<p>Remember that the initialization parameter is an escaping closure called <code class="language-plaintext highlighter-rouge">thunk</code>. This closure is only ever run once for the lifetime that my <code class="language-plaintext highlighter-rouge">DownloadProgressView</code>’s associated UI object is displayed on screen. The instance returned from the one-time execution of <code class="language-plaintext highlighter-rouge">thunk()</code> is what gets supplied to the SwiftUI views. But my <code class="language-plaintext highlighter-rouge">DownloadProgressView</code>’s <strong>initializer</strong> will be run <strong>many, many times</strong>, as often as SwiftUI needs. Each time, except for the very first initialization, all those <code class="language-plaintext highlighter-rouge">DownloadProgressObserver</code> objects that my <code class="language-plaintext highlighter-rouge">init</code> body is creating end up getting discarded as soon as they’re created. If anything expensive happens inside of <code class="language-plaintext highlighter-rouge">DownloadProgressObserver.init(episode:)</code>, that’s a ton of needless work that could degrade performance at best, or at worst, could introduce unwanted side effects (like mutating some state somewhere else).</p>

<p>The only safe and correct way to explicitly initialize a <code class="language-plaintext highlighter-rouge">@StateObject</code> is to place your wrapped object’s initialization inside the autoclosure:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">DownloadProgressView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@StateObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">observer</span><span class="p">:</span> <span class="kt">DownloadProgressObserver</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">episode</span><span class="p">:</span> <span class="kt">Episode</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">_observer</span> <span class="o">=</span> <span class="kt">StateObject</span><span class="p">(</span><span class="nv">wrappedValue</span><span class="p">:</span>
            <span class="kt">DownloadProgressObserver</span><span class="p">(</span><span class="nv">episode</span><span class="p">:</span> <span class="n">episode</span><span class="p">)</span>
        <span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That ensures that the object is initialized exactly once. This is particularly important to remember if preparing your wrapped object requires several statements:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">MyView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@StateObject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">tricky</span><span class="p">:</span> <span class="kt">SomethingTricky</span>
    
    <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">model</span><span class="p">:</span> <span class="kt">Model</span><span class="p">,</span> <span class="n">_</span> <span class="nv">baz</span><span class="p">:</span> <span class="kt">Baz</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">_tricky</span> <span class="o">=</span> <span class="kt">StateObject</span><span class="p">(</span><span class="nv">wrappedValue</span><span class="p">:</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">foo</span> <span class="o">=</span> <span class="kt">Foo</span><span class="p">(</span><span class="nv">model</span><span class="p">:</span> <span class="n">model</span><span class="p">)</span>
            <span class="k">let</span> <span class="nv">bar</span> <span class="o">=</span> <span class="kt">Bar</span><span class="p">(</span><span class="nv">model</span><span class="p">:</span> <span class="n">model</span><span class="p">)</span>
            <span class="k">let</span> <span class="nv">tricky</span> <span class="o">=</span> <span class="kt">SomethingTricky</span><span class="p">(</span>
                <span class="nv">foo</span><span class="p">:</span> <span class="n">foo</span><span class="p">,</span>
                <span class="nv">bar</span><span class="p">:</span> <span class="n">bar</span><span class="p">,</span>
                <span class="nv">baz</span><span class="p">:</span> <span class="n">baz</span>
            <span class="p">)</span>
            <span class="k">return</span> <span class="n">tricky</span>
        <span class="p">}())</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It would be natural to want to write all those statements in the main <code class="language-plaintext highlighter-rouge">init</code> body, and then pass the <code class="language-plaintext highlighter-rouge">tricky</code> instance as the <code class="language-plaintext highlighter-rouge">wrappedValue: tricky</code> parameter, but that would be wrong.</p>

<p>Hopefully I’ve just saved you an hour (or more) of fretful debugging. Apple, to their credit, <a href="https://developer.apple.com/documentation/swiftui/stateobject#Initialize-state-objects-using-external-data">did include warnings and gotchas in their documentation</a>, which I could have read before hand, but didn’t think to read it:</p>

<blockquote>
  <p>If the initial state of a state object depends on external data, you can call this initializer directly. However, use caution when doing this, because SwiftUI only initializes the object once during the lifetime of the view — even if you call the state object initializer more than once — which might result in unexpected behavior. For more information and an example, see StateObject.</p>
</blockquote>

<p>Side note: this whole debacle I created for myself points out the risks of using <code class="language-plaintext highlighter-rouge">@autoclosure</code> parameters. Unless one pauses on a code completion and jumps into the definition, it’s very, very easy to mistake an autoclosure for a plain-old parameter. An end user like me is not entirely to blame for my mistake, given how most (all?) other property wrappers in SwiftUI do not use autoclosures.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[I’m going to share some best practices when using @StateObject property wrappers, things learned the hard way, via some bugs that were difficult to diagnose and nearly impossible to notice during code review—unless one knows what to look for.]]></summary></entry><entry><title type="html">Scaled Metric Surprises on iOS &amp;amp; iPadOS</title><link href="https://jaredsinclair.com/2024/03/02/scaled-metrics.html" rel="alternate" type="text/html" title="Scaled Metric Surprises on iOS &amp;amp; iPadOS" /><published>2024-03-02T11:00:00+00:00</published><updated>2024-03-02T11:00:00+00:00</updated><id>https://jaredsinclair.com/2024/03/02/scaled-metrics</id><content type="html" xml:base="https://jaredsinclair.com/2024/03/02/scaled-metrics.html"><![CDATA[<p>UIKit’s <a href="https://developer.apple.com/documentation/uikit/uifontmetrics/2877388-scaledvalue"><code class="language-plaintext highlighter-rouge">UIFontMetrics.scaledValue(for:)</code></a> and SwiftUI’s <a href="https://developer.apple.com/documentation/swiftui/scaledmetric"><code class="language-plaintext highlighter-rouge">@ScaledMetric</code></a> property wrapper offer third-party developers a public means to scale arbitrary design reference values up and down relative to dynamic type size changes. Please be aware, however, that scaled values are not scaled proportionally with the default point sizes of the related text style. They scale according to some other scaling function that differs considerably from the related text style. An example will help illustrate this. Consider the following code:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">metrics</span> <span class="o">=</span> <span class="kt">UIFontMetrics</span><span class="p">(</span><span class="nv">forTextStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">body</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">size</span> <span class="o">=</span> <span class="n">metrics</span><span class="o">.</span><span class="nf">scaledValue</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="mf">17.0</span><span class="p">)</span>
</code></pre></div></div>

<p>If the device is set to the <code class="language-plaintext highlighter-rouge">.large</code> dynamic type setting, the identity value is returned (<code class="language-plaintext highlighter-rouge">size == 17.0</code>). If you then downscale the device’s dynamic type size setting to <code class="language-plaintext highlighter-rouge">.medium</code>, one might expect the returned value to be <code class="language-plaintext highlighter-rouge">16.0</code>. After all, the default system <code class="language-plaintext highlighter-rouge">.body</code> font size at <code class="language-plaintext highlighter-rouge">.large</code> is exactly <code class="language-plaintext highlighter-rouge">17.0</code>, and the default <code class="language-plaintext highlighter-rouge">.body</code> font size at <code class="language-plaintext highlighter-rouge">.medium</code> is exactly <code class="language-plaintext highlighter-rouge">16.0</code>. But instead, this is what happens:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// current dynamic type size setting is .medium...</span>
<span class="k">let</span> <span class="nv">metrics</span> <span class="o">=</span> <span class="kt">UIFontMetrics</span><span class="p">(</span><span class="nv">forTextStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">body</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">size</span> <span class="o">=</span> <span class="n">metrics</span><span class="o">.</span><span class="nf">scaledValue</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="mf">17.0</span><span class="p">)</span>
<span class="c1">// size == 16.333...</span>
</code></pre></div></div>

<p>The divergence is even more pronounced the further up/down the dynamic type size range one goes:</p>

<p class="inline-image"><a href="https://jaredsinclair.com/assets/img/quick-brown-fox.png"><img src="https://jaredsinclair.com/assets/img/quick-brown-fox.png" /></a></p>

<p>The red text in each pairing the above is the system default <code class="language-plaintext highlighter-rouge">.body</code> font, and the black text is a system body font obtained using a <code class="language-plaintext highlighter-rouge">ScaledMetric</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@ScaledMetric</span><span class="p">(</span><span class="nv">relativeTo</span><span class="p">:</span> <span class="o">.</span><span class="n">body</span><span class="p">)</span> <span class="k">var</span> <span class="nv">scaled</span> <span class="o">=</span> <span class="mf">17.0</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">Text</span><span class="p">(</span><span class="s">"Quick, brown fox."</span><span class="p">)</span>
        <span class="o">.</span><span class="nf">foregroundStyle</span><span class="p">(</span><span class="o">.</span><span class="n">red</span><span class="p">)</span>
        <span class="o">.</span><span class="nf">font</span><span class="p">(</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
        <span class="o">.</span><span class="n">overlay</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="s">"Quick, brown fox."</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">foregroundStyle</span><span class="p">(</span><span class="o">.</span><span class="n">black</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">font</span><span class="p">(</span><span class="o">.</span><span class="nf">system</span><span class="p">(</span><span class="nv">size</span><span class="p">:</span> <span class="n">scaled</span><span class="p">))</span>
        <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So if you need to scale a bit of UI in exact proportion to the <code class="language-plaintext highlighter-rouge">.body</code> font size, avoid using ScaledMetric/UIFontMetrics scaling APIs and instead directly obtain the <code class="language-plaintext highlighter-rouge">pointSize</code> from a system body font and use that value instead:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">UIFont</span><span class="o">.</span><span class="nf">preferredFont</span><span class="p">(</span>
    <span class="nv">forTextStyle</span><span class="p">:</span> <span class="o">.</span><span class="n">body</span><span class="p">,</span>
    <span class="nv">compatibleWith</span><span class="p">:</span> <span class="n">traitCollection</span>
<span class="p">)</span><span class="o">.</span><span class="n">pointSize</span>
</code></pre></div></div>

<p class="inline-image"><a href="https://jaredsinclair.com/assets/img/quicker-brown-fox.png"><img src="https://jaredsinclair.com/assets/img/quicker-brown-fox.png" /></a></p>

<p><em>(EDITED: An earlier version of this post included an erroneous assumption that has been living rent-free in my brain for years. Thanks to <a href="https://mindly.social/@myell0w/112027663158548168">Matthias</a> for the clarification.)</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[UIKit’s UIFontMetrics.scaledValue(for:) and SwiftUI’s @ScaledMetric property wrapper offer third-party developers a public means to scale arbitrary design reference values up and down relative to dynamic type size changes. Please be aware, however, that scaled values are not scaled proportionally with the default point sizes of the related text style. They scale according to some other scaling function that differs considerably from the related text style. An example will help illustrate this. Consider the following code:]]></summary></entry><entry><title type="html">Dagmar Chili Pitas &amp;amp; Doxowox: Now With Slightly-Less Unofficial Mirrors</title><link href="https://jaredsinclair.com/2023/10/29/dagmar-doxo.html" rel="alternate" type="text/html" title="Dagmar Chili Pitas &amp;amp; Doxowox: Now With Slightly-Less Unofficial Mirrors" /><published>2023-10-29T11:00:00+00:00</published><updated>2023-10-29T11:00:00+00:00</updated><id>https://jaredsinclair.com/2023/10/29/dagmar-doxo</id><content type="html" xml:base="https://jaredsinclair.com/2023/10/29/dagmar-doxo.html"><![CDATA[<p>It is with great pleasure that I share that the following two Internet classics have been brought back to life at their own blessed domain names, with TLS and everything, honest to God 21st century websites:</p>

<ul>
  <li>
    <p><a href="https://dagmarchili-pitas.com/index-original.html">Dagmar Chili Pitas</a>, the Two-Year Chili, demonstrably the peak of what’s possible with Hypertext Markup Language as a medium. The original domain name (<em>dagmarchili.pitas.com</em>) wasn’t available. In the years since The Chili went offline, <em>pitas.com</em> has changed hands. It’s replacement uses a hyphenated URL instead. ….the enemy of the good and all that.</p>
  </li>
  <li>
    <p><a href="https://doxowox.org">Doxowox</a>, the older sibling to The Chili, a beloved precursor, has also been resurrected. Enjoy!</p>
  </li>
</ul>

<p>Reader please note: these are <strong>not</strong> my writings, but I am honored to steward their continued presence on the Internet. They are works of art and deserve grace and aesthetic elbow room.</p>

<p>{}oe|e|ep[]</p>]]></content><author><name></name></author><summary type="html"><![CDATA[It is with great pleasure that I share that the following two Internet classics have been brought back to life at their own blessed domain names, with TLS and everything, honest to God 21st century websites:]]></summary></entry><entry><title type="html">Deep Fake</title><link href="https://jaredsinclair.com/2022/11/11/deep-fakes-repost-nov-22.html" rel="alternate" type="text/html" title="Deep Fake" /><published>2022-11-11T02:35:30+00:00</published><updated>2022-11-11T02:35:30+00:00</updated><id>https://jaredsinclair.com/2022/11/11/deep-fakes-repost-nov-22</id><content type="html" xml:base="https://jaredsinclair.com/2022/11/11/deep-fakes-repost-nov-22.html"><![CDATA[<p>Deleted.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Deleted.]]></summary></entry><entry><title type="html">Deep Fake</title><link href="https://jaredsinclair.com/2021/04/12/deep-fake.html" rel="alternate" type="text/html" title="Deep Fake" /><published>2021-04-12T19:18:20+00:00</published><updated>2021-04-12T19:18:20+00:00</updated><id>https://jaredsinclair.com/2021/04/12/deep-fake</id><content type="html" xml:base="https://jaredsinclair.com/2021/04/12/deep-fake.html"><![CDATA[<p>Deleted.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Deleted.]]></summary></entry><entry><title type="html">Mustang</title><link href="https://jaredsinclair.com/2021/04/01/mustang.html" rel="alternate" type="text/html" title="Mustang" /><published>2021-04-01T16:31:59+00:00</published><updated>2021-04-01T16:31:59+00:00</updated><id>https://jaredsinclair.com/2021/04/01/mustang</id><content type="html" xml:base="https://jaredsinclair.com/2021/04/01/mustang.html"><![CDATA[<p>Deleted.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Deleted.]]></summary></entry><entry><title type="html">Four Years Ago</title><link href="https://jaredsinclair.com/2020/11/03/four-years-ago.html" rel="alternate" type="text/html" title="Four Years Ago" /><published>2020-11-03T16:31:59+00:00</published><updated>2020-11-03T16:31:59+00:00</updated><id>https://jaredsinclair.com/2020/11/03/four-years-ago</id><content type="html" xml:base="https://jaredsinclair.com/2020/11/03/four-years-ago.html"><![CDATA[<p>Four years ago today I was in Cupertino for the eve of what was supposed to be a small day of personal achievement. I was part of a special project for the fruit company that I can’t discuss. The kickoff was in the morning, November 4th. I scarcely contained my giddy excitement.</p>

<p>I spent the evening at a hotel nearby, watching the returns roll in. The fuck is all that red on the map. When denial turned into dawning realization, and dawning realization decayed into despair, I violently switched off the television. I needed a drink, and not to be alone.</p>

<p>The plan was simple: wander until I encountered a bar. Any bar. It didn’t take long to stumble into one. Cramped inside, a handful of patrons. The horror show glared at us from the TV hanging under a drop-tile ceiling. Inescapable. What’re you gonna do.</p>

<p>“Jack. Neat.”</p>

<p>One shot glass turned into two, then three. It wasn’t enough. I once got booted in the nuts with so much force that I was propelled into the air. This felt worse. It was like watching God die on live TV, a melancholy accident, a <a href="https://www.youtube.com/watch?v=p-MNSLsjjdo">wave function collapsing</a> the wrong direction.</p>

<p>I wasn’t alone. An assembly line of grief had gathered there, nursing our shared world-historical wound. Time slipped off on a conveyer belt of booze. The bar tender had stopped charging for drinks and was robotically refilling everyone’s shot glasses as soon as they’d emptied.</p>

<p>The hangover the next morning was like none before. The hazy California sky was corpse-blue and relentless. Our little fruit project group assembled and rolled into the parking lot at the mothership. Where was the joy I’d anticipated all these years?</p>

<p>Friendly corporate busy-bees escorted us through electronically sealed doors, through gray hallways, past giant portraits of products in brushed metal and curved glass, and into a windowless room. Not enough chairs, so I sat legs-crossed in the corner leaning against a couch.</p>

<p>The next four hours demanded more of me than any software project I’ve ever been on, not in itself, but in context. We weren’t even writing code that day, merely setting up accounts and emails and corporate network access. Mind-numbing work, but nearly impossible that morning.</p>

<p>The obscenity of the night before clouded my vision. The sensible pale walls and polite gray carpeted floors mixed with the mind-numbing dullness of awaiting two-factor authentication codes, all giving way to a pall of grief, every keystroke a miracle of survival instincts.</p>

<p>Four years later I’m girding myself for another boot to the nuts, tallying the liquor stocks in the house, keeping my mind’s eye on the shelf where the shot glass waits patiently for a call to arms. Let’s hope it remains there this time around.</p>

<p>(Originally a <a href="https://twitter.com/jaredsinclair/status/1323643837470908416?s=21">Twitter thread</a>.)</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Four years ago today I was in Cupertino for the eve of what was supposed to be a small day of personal achievement. I was part of a special project for the fruit company that I can’t discuss. The kickoff was in the morning, November 4th. I scarcely contained my giddy excitement.]]></summary></entry></feed>