<?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://www.eliaszsawicki.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.eliaszsawicki.com/" rel="alternate" type="text/html" /><updated>2026-08-17T06:05:11+00:00</updated><id>https://www.eliaszsawicki.com/feed.xml</id><title type="html">Eliasz Sawicki</title><subtitle>Build a Jekyll blog in minutes, without touching the command line.</subtitle><entry><title type="html">Xcode Build Settings: Quick Help Shows You the Actual Key</title><link href="https://www.eliaszsawicki.com/build-settings-key-inspector/" rel="alternate" type="text/html" title="Xcode Build Settings: Quick Help Shows You the Actual Key" /><published>2026-08-17T00:00:00+00:00</published><updated>2026-08-17T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/build-settings-key-inspector</id><content type="html" xml:base="https://www.eliaszsawicki.com/build-settings-key-inspector/"><![CDATA[<p>Build Settings shows friendly names like “Swift Language Version,” but <code class="language-plaintext highlighter-rouge">.xcconfig</code> files and <code class="language-plaintext highlighter-rouge">xcodebuild</code> need the raw key. Option-click the setting (or select it and open the Quick Help inspector, ⌥⌘2/⌘⌥3) and the key shows up right there.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Swift Language Version → SWIFT_VERSION
Enable Bitcode          → ENABLE_BITCODE
Other Linker Flags      → OTHER_LDFLAGS
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Debug.xcconfig
SWIFT_VERSION = 5.0
</code></pre></div></div>

<p><strong>Takeaway:</strong> don’t guess or grep the internet for a setting’s underlying name — option-click it in the Build Settings editor and Quick Help gives you the exact key to drop into an <code class="language-plaintext highlighter-rouge">.xcconfig</code>.</p>]]></content><author><name></name></author><category term="devlog" /><category term="xcode" /><category term="build-settings" /><summary type="html"><![CDATA[Build Settings shows friendly names like “Swift Language Version,” but .xcconfig files and xcodebuild need the raw key. Option-click the setting (or select it and open the Quick Help inspector, ⌥⌘2/⌘⌥3) and the key shows up right there.]]></summary></entry><entry><title type="html">Xcode Schemes: Launch Arguments Silently Override UserDefaults</title><link href="https://www.eliaszsawicki.com/scheme-launch-arguments-userdefaults/" rel="alternate" type="text/html" title="Xcode Schemes: Launch Arguments Silently Override UserDefaults" /><published>2026-08-10T00:00:00+00:00</published><updated>2026-08-10T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/scheme-launch-arguments-userdefaults</id><content type="html" xml:base="https://www.eliaszsawicki.com/scheme-launch-arguments-userdefaults/"><![CDATA[<p><code class="language-plaintext highlighter-rouge">Edit Scheme → Run → Arguments → Arguments Passed On Launch</code> isn’t just for <code class="language-plaintext highlighter-rouge">-AppleLanguage</code>-style system flags. Anything typed there as <code class="language-plaintext highlighter-rouge">-key value</code> gets registered into <code class="language-plaintext highlighter-rouge">NSArgumentDomain</code>, and that domain outranks everything else in <code class="language-plaintext highlighter-rouge">UserDefaults</code>’ search order — including values you explicitly wrote with <code class="language-plaintext highlighter-rouge">.set()</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-disableAnimations YES
-apiEnvironment staging
</code></pre></div></div>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">UserDefaults</span><span class="o">.</span><span class="n">standard</span><span class="o">.</span><span class="nf">bool</span><span class="p">(</span><span class="nv">forKey</span><span class="p">:</span> <span class="s">"disableAnimations"</span><span class="p">)</span> <span class="c1">// true, no code change needed</span>
<span class="kt">UserDefaults</span><span class="o">.</span><span class="n">standard</span><span class="o">.</span><span class="nf">string</span><span class="p">(</span><span class="nv">forKey</span><span class="p">:</span> <span class="s">"apiEnvironment"</span><span class="p">)</span>   <span class="c1">// "staging"</span>
</code></pre></div></div>

<p>Even if the app has previously called <code class="language-plaintext highlighter-rouge">UserDefaults.standard.set(false, forKey: "disableAnimations")</code> and that’s sitting on disk, the scheme argument wins on every launch — it doesn’t persist, it just shadows whatever’s stored for the life of that process.</p>

<p>Non-string values need plist syntax, quoted so the shell doesn’t eat the parens:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-featureFlags '(chat, offlineSync)'
</code></pre></div></div>

<p><strong>Takeaway:</strong> for temporary overrides — feature flags, locale testing, pointing at a different API environment — duplicate the scheme and set the argument there instead of adding <code class="language-plaintext highlighter-rouge">#if DEBUG</code> branches or hand-editing defaults in the simulator. Switching schemes becomes the toggle.</p>]]></content><author><name></name></author><category term="devlog" /><category term="xcode" /><category term="ios" /><category term="debugging" /><category term="userdefaults" /><summary type="html"><![CDATA[Edit Scheme → Run → Arguments → Arguments Passed On Launch isn’t just for -AppleLanguage-style system flags. Anything typed there as -key value gets registered into NSArgumentDomain, and that domain outranks everything else in UserDefaults’ search order — including values you explicitly wrote with .set().]]></summary></entry><entry><title type="html">SwiftUI Group Lab: There’s No API for ‘On Top of Everything’</title><link href="https://www.eliaszsawicki.com/swiftui-overlay-everything/" rel="alternate" type="text/html" title="SwiftUI Group Lab: There’s No API for ‘On Top of Everything’" /><published>2026-08-03T00:00:00+00:00</published><updated>2026-08-03T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/swiftui-overlay-everything</id><content type="html" xml:base="https://www.eliaszsawicki.com/swiftui-overlay-everything/"><![CDATA[<p><em>Notes from a SwiftUI Group Lab Q&amp;A — a summary of what the panel said, not my own take.</em></p>

<p>Question: how do you present a full-screen overlay above <em>everything</em>, including whatever sheet or full-screen-cover is already up — say, a forced login screen after a network disconnect? Short answer from the panel: SwiftUI doesn’t have an API for that, and that’s arguably on purpose.</p>

<p>Presentations in SwiftUI form a hierarchy. From the root, one <code class="language-plaintext highlighter-rouge">fullScreenCover</code> on top is easy:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">RootView</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">showLogin</span> <span class="o">=</span> <span class="kc">false</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">ContentView</span><span class="p">()</span>
            <span class="o">.</span><span class="nf">fullScreenCover</span><span class="p">(</span><span class="nv">isPresented</span><span class="p">:</span> <span class="err">$</span><span class="n">showLogin</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">LoginView</span><span class="p">()</span>
            <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>But if something else already presented a sheet or cover deeper in the hierarchy, you can’t just reach in from outside and stack another presentation above it — you’d need to track every active presentation yourself and coordinate through that, since SwiftUI has no built-in registry of “what’s currently on top.”</p>

<p>When that coordination isn’t worth building, the panel’s fallback is to drop to UIKit: with the UIKit scene lifecycle you get direct access to <code class="language-plaintext highlighter-rouge">UIWindowScene</code>, so you can spin up a new <code class="language-plaintext highlighter-rouge">UIWindow</code>, give it a <code class="language-plaintext highlighter-rouge">UIHostingController</code> root, and position it above everything — SwiftUI content included.</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">OverlayWindowController</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">overlayWindow</span><span class="p">:</span> <span class="kt">UIWindow</span><span class="p">?</span>

    <span class="kd">func</span> <span class="nf">showOverlay</span><span class="p">(</span><span class="k">in</span> <span class="nv">scene</span><span class="p">:</span> <span class="kt">UIWindowScene</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">window</span> <span class="o">=</span> <span class="kt">UIWindow</span><span class="p">(</span><span class="nv">windowScene</span><span class="p">:</span> <span class="n">scene</span><span class="p">)</span>
        <span class="n">window</span><span class="o">.</span><span class="n">rootViewController</span> <span class="o">=</span> <span class="kt">UIHostingController</span><span class="p">(</span><span class="nv">rootView</span><span class="p">:</span> <span class="kt">LoginView</span><span class="p">())</span>
        <span class="n">window</span><span class="o">.</span><span class="n">windowLevel</span> <span class="o">=</span> <span class="o">.</span><span class="n">alert</span> <span class="o">+</span> <span class="mi">1</span>
        <span class="n">window</span><span class="o">.</span><span class="nf">makeKeyAndVisible</span><span class="p">()</span>
        <span class="n">overlayWindow</span> <span class="o">=</span> <span class="n">window</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The catch: this doesn’t scale past one team doing it. If two parts of the app both decide their thing “always needs to be on top,” it degenerates into whichever <code class="language-plaintext highlighter-rouge">UIWindow</code> was made last winning — a distributed layering decision with no source of truth, which is exactly the kind of bug SwiftUI’s presentation model is designed to prevent.</p>

<p><strong>Takeaway:</strong> the panel’s real advice was to question the design brief before reaching for this. A forced full-screen cover over an arbitrary presentation state is disruptive to the user regardless of how it’s implemented — often the better fix is unwinding the navigation/presentation stack to a base state and restoring it after login, rather than layering on top of whatever’s currently shown. If you do need a global overlay, a single coordinated owner (not ad hoc <code class="language-plaintext highlighter-rouge">UIWindow</code>s from wherever) is what keeps it from becoming a last-window-wins race.</p>]]></content><author><name></name></author><category term="devlog" /><category term="swiftui" /><category term="ios" /><category term="presentation" /><summary type="html"><![CDATA[Notes from a SwiftUI Group Lab Q&amp;A — a summary of what the panel said, not my own take.]]></summary></entry><entry><title type="html">SwiftUI Group Lab: Spot Invalidations with a Random Background Color</title><link href="https://www.eliaszsawicki.com/swiftui-random-color-invalidation/" rel="alternate" type="text/html" title="SwiftUI Group Lab: Spot Invalidations with a Random Background Color" /><published>2026-07-27T00:00:00+00:00</published><updated>2026-07-27T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/swiftui-random-color-invalidation</id><content type="html" xml:base="https://www.eliaszsawicki.com/swiftui-random-color-invalidation/"><![CDATA[<p><em>Notes from a SwiftUI Group Lab Q&amp;A — a summary of what the panel said, not my own take.</em></p>

<p>Body invalidation in SwiftUI is invisible by default — a view can re-evaluate far more often than you’d guess, with nothing on screen to show it. The panel’s trick: give the view a random background color on every <code class="language-plaintext highlighter-rouge">body</code> evaluation, so each re-render literally flashes a different color.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">RowView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">item</span><span class="p">:</span> <span class="kt">Item</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="n">item</span><span class="o">.</span><span class="n">title</span><span class="p">)</span>
            <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
            <span class="o">.</span><span class="nf">background</span><span class="p">(</span><span class="kt">Color</span><span class="p">(</span>
                <span class="nv">red</span><span class="p">:</span> <span class="o">.</span><span class="nf">random</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="mi">0</span><span class="o">...</span><span class="mi">1</span><span class="p">),</span>
                <span class="nv">green</span><span class="p">:</span> <span class="o">.</span><span class="nf">random</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="mi">0</span><span class="o">...</span><span class="mi">1</span><span class="p">),</span>
                <span class="nv">blue</span><span class="p">:</span> <span class="o">.</span><span class="nf">random</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="mi">0</span><span class="o">...</span><span class="mi">1</span><span class="p">)</span>
            <span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since the color is generated inside <code class="language-plaintext highlighter-rouge">body</code>, it changes on every invalidation. A row that should only redraw when its own data changes but instead flickers on every parent update, scroll, or unrelated state change becomes obvious at a glance — no Instruments session required.</p>

<p><strong>Takeaway:</strong> wrap a suspect view’s background in a random color during development to turn invisible re-renders into a visible signal, then strip it out before shipping.</p>]]></content><author><name></name></author><category term="devlog" /><category term="swiftui" /><category term="performance" /><category term="ios" /><category term="debugging" /><summary type="html"><![CDATA[Notes from a SwiftUI Group Lab Q&amp;A — a summary of what the panel said, not my own take.]]></summary></entry><entry><title type="html">SwiftUI Group Lab: Track Visible Items, Not Scroll Offset</title><link href="https://www.eliaszsawicki.com/swiftui-infinite-scroll/" rel="alternate" type="text/html" title="SwiftUI Group Lab: Track Visible Items, Not Scroll Offset" /><published>2026-07-20T00:00:00+00:00</published><updated>2026-07-20T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/swiftui-infinite-scroll</id><content type="html" xml:base="https://www.eliaszsawicki.com/swiftui-infinite-scroll/"><![CDATA[<p><em>Notes from a SwiftUI Group Lab Q&amp;A — a summary of what the panel said, not my own take.</em></p>

<p>Treat a scroll view’s content offset as an implementation detail — with lazy stacks it’s only estimated from the heights of off-screen views and carries no reliable semantic meaning. Trigger UI changes off the views actually on screen instead.</p>

<p><code class="language-plaintext highlighter-rouge">scrollPosition</code> tracks which item is currently visible by ID, not by offset — so it stays relative to your data model rather than a pixel value:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">FeedView</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">position</span> <span class="o">=</span> <span class="kt">ScrollPosition</span><span class="p">(</span><span class="nv">idType</span><span class="p">:</span> <span class="kt">Item</span><span class="o">.</span><span class="kt">ID</span><span class="o">.</span><span class="k">self</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">ScrollView</span> <span class="p">{</span>
            <span class="kt">LazyVStack</span> <span class="p">{</span>
                <span class="kt">ForEach</span><span class="p">(</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span> <span class="n">item</span> <span class="k">in</span>
                    <span class="kt">ItemRow</span><span class="p">(</span><span class="nv">item</span><span class="p">:</span> <span class="n">item</span><span class="p">)</span><span class="o">.</span><span class="nf">id</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">scrollPosition</span><span class="p">(</span><span class="err">$</span><span class="n">position</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For visibility-based triggers — analytics, impression tracking — <code class="language-plaintext highlighter-rouge">onScrollVisibilityChange(threshold:)</code> fires when a view crosses a percentage-visible threshold, instead of you computing that from offsets yourself:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">ItemRow</span><span class="p">(</span><span class="nv">item</span><span class="p">:</span> <span class="n">item</span><span class="p">)</span>
    <span class="o">.</span><span class="nf">onScrollVisibilityChange</span><span class="p">(</span><span class="nv">threshold</span><span class="p">:</span> <span class="mf">0.8</span><span class="p">)</span> <span class="p">{</span> <span class="n">isVisible</span> <span class="k">in</span>
        <span class="k">if</span> <span class="n">isVisible</span> <span class="p">{</span> <span class="n">analytics</span><span class="o">.</span><span class="nf">logImpression</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">onGeometryChange</code> and scroll transitions round out the toolkit: the former replaces <code class="language-plaintext highlighter-rouge">GeometryReader</code> hacks for reading frame changes, the latter offloads scroll-driven visual effects to the system and is often cheaper than computing them by hand from offset.</p>

<p><strong>Infinite scrolling</strong>, per the panel, doesn’t need any offset math either — put a sentinel view at the end of the list and let its <code class="language-plaintext highlighter-rouge">onAppear</code> trigger the next page fetch:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">LazyVStack</span> <span class="p">{</span>
    <span class="kt">ForEach</span><span class="p">(</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span> <span class="n">item</span> <span class="k">in</span>
        <span class="kt">ItemRow</span><span class="p">(</span><span class="nv">item</span><span class="p">:</span> <span class="n">item</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="kt">Color</span><span class="o">.</span><span class="n">clear</span>
        <span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">height</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
        <span class="o">.</span><span class="n">onAppear</span> <span class="p">{</span> <span class="kt">Task</span> <span class="p">{</span> <span class="k">await</span> <span class="nf">loadMore</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Takeaway:</strong> stop reasoning about scroll views in terms of content offset. Whatever you’re after — revealing a view at some scroll depth, tracking impressions, or paging in more data — there’s an API keyed to the views on screen instead.</p>]]></content><author><name></name></author><category term="devlog" /><category term="swiftui" /><category term="performance" /><category term="ios" /><summary type="html"><![CDATA[Notes from a SwiftUI Group Lab Q&amp;A — a summary of what the panel said, not my own take.]]></summary></entry><entry><title type="html">Swift Testing: Test.cancel()</title><link href="https://www.eliaszsawicki.com/swift-testing-test-cancel/" rel="alternate" type="text/html" title="Swift Testing: Test.cancel()" /><published>2026-07-13T00:00:00+00:00</published><updated>2026-07-13T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/swift-testing-test-cancel</id><content type="html" xml:base="https://www.eliaszsawicki.com/swift-testing-test-cancel/"><![CDATA[<p>Swift Testing 2.0 (Swift 6.4) adds <code class="language-plaintext highlighter-rouge">Test.cancel()</code> — a way to programmatically cancel the currently running test, marking it as cancelled rather than failed.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@Test</span> <span class="kd">func</span> <span class="nf">featureRequiresDevice</span><span class="p">()</span> <span class="k">async</span> <span class="k">throws</span> <span class="p">{</span>
    <span class="k">guard</span> <span class="k">await</span> <span class="kt">DeviceCapability</span><span class="o">.</span><span class="n">isSupported</span> <span class="k">else</span> <span class="p">{</span>
        <span class="kt">Test</span><span class="o">.</span><span class="n">current</span><span class="p">?</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
        <span class="k">return</span>
    <span class="p">}</span>
    <span class="c1">// test body runs only when supported</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is different from <code class="language-plaintext highlighter-rouge">#require</code> (which records a failure) or throwing a <code class="language-plaintext highlighter-rouge">Skip</code> (which marks the test skipped). <code class="language-plaintext highlighter-rouge">Test.cancel()</code> cancels the underlying task — the test shows up as cancelled in the report, signalling “not applicable” rather than “broken.”</p>

<p>Because it goes through Swift concurrency cancellation, any <code class="language-plaintext highlighter-rouge">try await</code> after the call will throw <code class="language-plaintext highlighter-rouge">CancellationError</code> — so a bare <code class="language-plaintext highlighter-rouge">cancel()</code> followed by <code class="language-plaintext highlighter-rouge">return</code> is the safest pattern.</p>

<p>Cancellation also propagates to child tests in a suite:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@Test</span> <span class="kd">func</span> <span class="nf">suite</span><span class="p">()</span> <span class="k">async</span> <span class="p">{</span>
    <span class="k">if</span> <span class="kt">ProcessInfo</span><span class="o">.</span><span class="n">processInfo</span><span class="o">.</span><span class="n">environment</span><span class="p">[</span><span class="s">"CI"</span><span class="p">]</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
        <span class="kt">Test</span><span class="o">.</span><span class="n">current</span><span class="p">?</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
        <span class="k">return</span>
    <span class="p">}</span>
    <span class="c1">// child tests run only in CI</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Takeaway:</strong> use <code class="language-plaintext highlighter-rouge">Test.cancel()</code> when a test is conditionally irrelevant — not wrong, just not applicable. Reserve <code class="language-plaintext highlighter-rouge">#require</code> for preconditions that <em>should</em> hold, and keep <code class="language-plaintext highlighter-rouge">cancel()</code> for environment or capability gates.</p>]]></content><author><name></name></author><category term="devlog" /><category term="swift" /><category term="testing" /><category term="swift6" /><summary type="html"><![CDATA[Swift Testing 2.0 (Swift 6.4) adds Test.cancel() — a way to programmatically cancel the currently running test, marking it as cancelled rather than failed.]]></summary></entry><entry><title type="html">Swift 6.4: Task Cancellation Shields</title><link href="https://www.eliaszsawicki.com/task-cancellation-shield/" rel="alternate" type="text/html" title="Swift 6.4: Task Cancellation Shields" /><published>2026-07-06T00:00:00+00:00</published><updated>2026-07-06T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/task-cancellation-shield</id><content type="html" xml:base="https://www.eliaszsawicki.com/task-cancellation-shield/"><![CDATA[<p>SE-0504 adds <code class="language-plaintext highlighter-rouge">withTaskCancellationShield</code> — a way to run a closure where <code class="language-plaintext highlighter-rouge">Task.isCancelled</code> always returns <code class="language-plaintext highlighter-rouge">false</code>, regardless of the outer task’s cancellation state.</p>

<p>The problem it solves: cleanup and rollback work that <em>must</em> finish even when a task has been cancelled. Without a shield, awaits inside cancelled tasks can skip or short-circuit unexpectedly.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">closeConnection</span><span class="p">()</span> <span class="k">async</span> <span class="p">{</span>
    <span class="k">await</span> <span class="n">withTaskCancellationShield</span> <span class="p">{</span>
        <span class="k">await</span> <span class="n">database</span><span class="o">.</span><span class="nf">close</span><span class="p">()</span>   <span class="c1">// runs to completion even if outer task is cancelled</span>
    <span class="p">}</span>
    <span class="c1">// Task.isCancelled reflects the real state again here</span>
<span class="p">}</span>
</code></pre></div></div>

<p>A good one-two punch is pairing it with <code class="language-plaintext highlighter-rouge">defer</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">performWork</span><span class="p">()</span> <span class="k">async</span> <span class="k">throws</span> <span class="p">{</span>
    <span class="k">defer</span> <span class="p">{</span>
        <span class="n">withTaskCancellationShield</span> <span class="p">{</span>
            <span class="nf">cleanup</span><span class="p">()</span>   <span class="c1">// deferred cleanup that must not be skipped</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">try</span> <span class="k">await</span> <span class="nf">doTheWork</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The shield also prevents cancellation from propagating into child tasks — <code class="language-plaintext highlighter-rouge">async let</code> bindings and task groups inside the closure won’t be automatically cancelled.</p>

<p><strong>Constraints worth knowing:</strong></p>
<ul>
  <li>Keep shielded regions short — finish or roll back work already started, not hide cancellation from long-running operations</li>
  <li>Once the closure returns, the outer task’s cancellation state is restored</li>
</ul>

<p><strong>Takeaway:</strong> reach for <code class="language-plaintext highlighter-rouge">withTaskCancellationShield</code> when you have cleanup that cannot be skipped on cancellation — it’s the missing piece for reliable resource teardown in structured concurrency.</p>]]></content><author><name></name></author><category term="devlog" /><category term="swift" /><category term="concurrency" /><category term="swift6" /><summary type="html"><![CDATA[SE-0504 adds withTaskCancellationShield — a way to run a closure where Task.isCancelled always returns false, regardless of the outer task’s cancellation state.]]></summary></entry><entry><title type="html">Trust Insights: Detecting Coerced Actions in iOS 27</title><link href="https://www.eliaszsawicki.com/trust-insights/" rel="alternate" type="text/html" title="Trust Insights: Detecting Coerced Actions in iOS 27" /><published>2026-06-29T00:00:00+00:00</published><updated>2026-06-29T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/trust-insights</id><content type="html" xml:base="https://www.eliaszsawicki.com/trust-insights/"><![CDATA[<p>Summary of WWDC2026 session about:
Trust Insights (iOS 27) — behavioral coercion detection framework.</p>

<p><strong>The problem it solves:</strong> Social engineering attacks where the user performs the action themselves (authenticated, legitimate), so MFA/biometrics are useless. Need a signal for coerced vs. genuine intent.</p>

<p><strong>Integration flow:</strong></p>
<ul>
  <li>Requires an entitlement/capability in Xcode</li>
  <li>Client-side Swift API only (no server integration needed)</li>
  <li>Create <code class="language-plaintext highlighter-rouge">InsightContext</code> with <code class="language-plaintext highlighter-rouge">operationCategory</code> (payment, account, resourceUse, communication, other) + <code class="language-plaintext highlighter-rouge">InsightEvaluation</code></li>
  <li>Call <code class="language-plaintext highlighter-rouge">requestEvaluation()</code> async — takes a few seconds, needs internet</li>
  <li>Check authorization status first (user can disable it in Settings)</li>
</ul>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">guard</span> <span class="k">await</span> <span class="kt">InsightContext</span><span class="o">.</span><span class="n">authorizationStatus</span> <span class="o">==</span> <span class="o">.</span><span class="n">authorized</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>

<span class="k">let</span> <span class="nv">context</span> <span class="o">=</span> <span class="kt">InsightContext</span><span class="p">(</span>
    <span class="nv">operationCategory</span><span class="p">:</span> <span class="o">.</span><span class="n">payment</span><span class="p">,</span>
    <span class="nv">evaluation</span><span class="p">:</span> <span class="kt">InsightEvaluation</span><span class="p">()</span>
<span class="p">)</span>
<span class="k">let</span> <span class="nv">insight</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="n">context</span><span class="o">.</span><span class="nf">requestEvaluation</span><span class="p">()</span>

<span class="k">switch</span> <span class="n">insight</span><span class="o">.</span><span class="n">isLikelyBeingCoached</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">high</span><span class="p">:</span>    <span class="nf">showCoercionWarning</span><span class="p">()</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">medium</span><span class="p">:</span>  <span class="nf">requireAdditionalVerification</span><span class="p">()</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">unknown</span><span class="p">:</span> <span class="k">break</span>  <span class="c1">// no evidence — not a green light</span>
<span class="p">}</span>

<span class="n">insight</span><span class="o">.</span><span class="nf">reportConsumption</span><span class="p">()</span>  <span class="c1">// mandatory</span>
</code></pre></div></div>

<p><strong>Result values for <code class="language-plaintext highlighter-rouge">IsLikelyBeingCoachedInsight</code>:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">unknown</code> — no evidence, but not to be treated as low risk</li>
  <li><code class="language-plaintext highlighter-rouge">medium</code> — introduce friction, additional verification, adjust risk scoring</li>
  <li><code class="language-plaintext highlighter-rouge">high</code> — warn the user before proceeding</li>
</ul>

<p><strong>Feedback (mandatory):</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">reportConsumption()</code> must be called per evaluation or you get rate-limited</li>
  <li>Offline fraud labels submitted via Apple Business Register (server-to-server) — optional but helps the model</li>
</ul>

<p><strong>Privacy architecture:</strong></p>
<ul>
  <li>Device-sourced signals (interaction patterns, timing, sensors) processed locally, inputs discarded immediately</li>
  <li>Only the single output value leaves the device</li>
  <li>Apple Account signals + velocity checks may be incorporated server-side</li>
  <li>Never touches Photos, Messages, Mail content</li>
  <li>User can disable in Settings (cooldown applies to prevent coaching into disabling it)</li>
</ul>

<p><strong>Best practices:</strong></p>
<ul>
  <li>Use it at high-value moments: P2P payments, irreversible actions, remote access grants, sensitive data sharing</li>
  <li>Never sole decision factor — integrate into existing risk logic</li>
  <li>Never treat unknown or missing value as safe</li>
  <li>Test with Xcode scheme launch argument overrides (sandbox in dev, production on App Store)</li>
</ul>

<p>Related: App Attest for verifying requests come from legitimate app instances.</p>]]></content><author><name></name></author><category term="devlog" /><category term="ios" /><category term="security" /><category term="swift" /><category term="fraud" /><summary type="html"><![CDATA[Summary of WWDC2026 session about: Trust Insights (iOS 27) — behavioral coercion detection framework.]]></summary></entry><entry><title type="html">Swift 6.2 Span: Safe, Non-Owning Buffer Views</title><link href="https://www.eliaszsawicki.com/swift-span/" rel="alternate" type="text/html" title="Swift 6.2 Span: Safe, Non-Owning Buffer Views" /><published>2026-06-22T00:00:00+00:00</published><updated>2026-06-22T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/swift-span</id><content type="html" xml:base="https://www.eliaszsawicki.com/swift-span/"><![CDATA[<p><code class="language-plaintext highlighter-rouge">Span&lt;T&gt;</code> is Swift 6.2’s answer to <code class="language-plaintext highlighter-rouge">UnsafeBufferPointer</code>: a bounds-checked, non-owning view into a contiguous region of memory — with lifetime safety enforced by the compiler.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">numbers</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
<span class="k">let</span> <span class="nv">span</span><span class="p">:</span> <span class="kt">Span</span><span class="o">&lt;</span><span class="kt">Int</span><span class="o">&gt;</span> <span class="o">=</span> <span class="n">numbers</span><span class="o">.</span><span class="n">span</span>   <span class="c1">// zero-copy view, no allocation</span>

<span class="nf">print</span><span class="p">(</span><span class="n">span</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>      <span class="c1">// 1</span>
<span class="nf">print</span><span class="p">(</span><span class="n">span</span><span class="o">.</span><span class="n">count</span><span class="p">)</span>   <span class="c1">// 5</span>

<span class="k">for</span> <span class="n">value</span> <span class="k">in</span> <span class="n">span</span> <span class="p">{</span>
    <span class="nf">process</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The key property is that <code class="language-plaintext highlighter-rouge">Span</code> is <code class="language-plaintext highlighter-rouge">~Escapable</code> — it cannot outlive the storage it views. The compiler rejects any attempt to escape it:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">leakedSpan</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Span</span><span class="o">&lt;</span><span class="kt">Int</span><span class="o">&gt;</span> <span class="p">{</span>   <span class="c1">// ❌ Span&lt;Int&gt; is non-escapable</span>
    <span class="k">let</span> <span class="nv">numbers</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">numbers</span><span class="o">.</span><span class="n">span</span>             <span class="c1">// compile error: span would outlive numbers</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This makes the entire class of dangling-pointer bugs a compile-time error rather than a runtime crash. Compare with the old approach:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Before: no safety net</span>
<span class="kd">func</span> <span class="nf">riskyWork</span><span class="p">(</span><span class="nv">buffer</span><span class="p">:</span> <span class="kt">UnsafeBufferPointer</span><span class="o">&lt;</span><span class="kt">Int</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="n">numbers</span><span class="o">.</span><span class="n">withUnsafeBufferPointer</span> <span class="p">{</span> <span class="nf">riskyWork</span><span class="p">(</span><span class="nv">buffer</span><span class="p">:</span> <span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>  <span class="c1">// easy to misuse</span>

<span class="c1">// Swift 6.2: lifetime tracked by the compiler</span>
<span class="kd">func</span> <span class="nf">safeWork</span><span class="p">(</span><span class="nv">span</span><span class="p">:</span> <span class="kt">Span</span><span class="o">&lt;</span><span class="kt">Int</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="nf">safeWork</span><span class="p">(</span><span class="nv">span</span><span class="p">:</span> <span class="n">numbers</span><span class="o">.</span><span class="n">span</span><span class="p">)</span>
</code></pre></div></div>

<p>For byte-level access there is <code class="language-plaintext highlighter-rouge">RawSpan</code>, and <code class="language-plaintext highlighter-rouge">MutableSpan&lt;T&gt;</code> covers write-enabled views (with the same lifetime guarantees).</p>

<p><strong>Takeaway:</strong> prefer <code class="language-plaintext highlighter-rouge">Span&lt;T&gt;</code> over <code class="language-plaintext highlighter-rouge">UnsafeBufferPointer</code> whenever you need a performant, zero-copy read over contiguous memory — the <code class="language-plaintext highlighter-rouge">~Escapable</code> constraint gives you the speed without the footguns.</p>]]></content><author><name></name></author><category term="devlog" /><category term="swift" /><category term="performance" /><category term="memory" /><category term="swift6" /><summary type="html"><![CDATA[Span&lt;T&gt; is Swift 6.2’s answer to UnsafeBufferPointer: a bounds-checked, non-owning view into a contiguous region of memory — with lifetime safety enforced by the compiler.]]></summary></entry><entry><title type="html">TCA: Injecting Different Reducers Into the Same View</title><link href="https://www.eliaszsawicki.com/tca-swappable-reducers/" rel="alternate" type="text/html" title="TCA: Injecting Different Reducers Into the Same View" /><published>2026-06-15T00:00:00+00:00</published><updated>2026-06-15T00:00:00+00:00</updated><id>https://www.eliaszsawicki.com/tca-swappable-reducers</id><content type="html" xml:base="https://www.eliaszsawicki.com/tca-swappable-reducers/"><![CDATA[<p>Sometimes the same UI needs different logic depending on context — a form that fetches data in production but returns stubs in previews, or a screen that behaves differently in two flows. In TCA you can handle this by making the view generic over its reducer.</p>

<p>The trick is constraining the generic parameter so the <code class="language-plaintext highlighter-rouge">State</code> and <code class="language-plaintext highlighter-rouge">Action</code> types match, while letting the concrete reducer vary at the call site.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">CounterView</span><span class="o">&lt;</span><span class="kt">R</span><span class="p">:</span> <span class="kt">Reducer</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">View</span> <span class="k">where</span> <span class="kt">R</span><span class="o">.</span><span class="kt">State</span> <span class="o">==</span> <span class="kt">CounterFeature</span><span class="o">.</span><span class="kt">State</span><span class="p">,</span>
                                            <span class="kt">R</span><span class="o">.</span><span class="kt">Action</span> <span class="o">==</span> <span class="kt">CounterFeature</span><span class="o">.</span><span class="kt">Action</span> <span class="p">{</span>
    <span class="kd">@Bindable</span> <span class="k">var</span> <span class="nv">store</span><span class="p">:</span> <span class="kt">Store</span><span class="o">&lt;</span><span class="kt">R</span><span class="o">.</span><span class="kt">State</span><span class="p">,</span> <span class="kt">R</span><span class="o">.</span><span class="kt">Action</span><span class="o">&gt;</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">store</span><span class="o">.</span><span class="n">count</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
            <span class="kt">Button</span><span class="p">(</span><span class="s">"+"</span><span class="p">)</span> <span class="p">{</span> <span class="n">store</span><span class="o">.</span><span class="nf">send</span><span class="p">(</span><span class="o">.</span><span class="n">increment</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>Now define two reducers with the same <code class="language-plaintext highlighter-rouge">State</code>/<code class="language-plaintext highlighter-rouge">Action</code> but different logic:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@Reducer</span>
<span class="kd">struct</span> <span class="kt">CounterFeature</span> <span class="p">{</span>
    <span class="kd">struct</span> <span class="kt">State</span><span class="p">:</span> <span class="kt">Equatable</span> <span class="p">{</span> <span class="k">var</span> <span class="nv">count</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">}</span>
    <span class="kd">enum</span> <span class="kt">Action</span> <span class="p">{</span> <span class="k">case</span> <span class="n">increment</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">Reducer</span><span class="o">&lt;</span><span class="kt">State</span><span class="p">,</span> <span class="kt">Action</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="kt">Reduce</span> <span class="p">{</span> <span class="n">state</span><span class="p">,</span> <span class="n">action</span> <span class="k">in</span>
            <span class="k">switch</span> <span class="n">action</span> <span class="p">{</span>
            <span class="k">case</span> <span class="o">.</span><span class="nv">increment</span><span class="p">:</span>
                <span class="n">state</span><span class="o">.</span><span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
                <span class="k">return</span> <span class="o">.</span><span class="k">none</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">@Reducer</span>
<span class="kd">struct</span> <span class="kt">PreviewCounterFeature</span> <span class="p">{</span>
    <span class="kd">typealias</span> <span class="kt">State</span> <span class="o">=</span> <span class="kt">CounterFeature</span><span class="o">.</span><span class="kt">State</span>
    <span class="kd">typealias</span> <span class="kt">Action</span> <span class="o">=</span> <span class="kt">CounterFeature</span><span class="o">.</span><span class="kt">Action</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">Reducer</span><span class="o">&lt;</span><span class="kt">State</span><span class="p">,</span> <span class="kt">Action</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="kt">Reduce</span> <span class="p">{</span> <span class="n">state</span><span class="p">,</span> <span class="n">action</span> <span class="k">in</span>
            <span class="n">state</span><span class="o">.</span><span class="n">count</span> <span class="o">+=</span> <span class="mi">10</span>
            <span class="k">return</span> <span class="o">.</span><span class="k">none</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Inject whichever reducer the context needs:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Production</span>
<span class="kt">CounterView</span><span class="p">(</span><span class="nv">store</span><span class="p">:</span> <span class="kt">Store</span><span class="p">(</span><span class="nv">initialState</span><span class="p">:</span> <span class="o">.</span><span class="nf">init</span><span class="p">())</span> <span class="p">{</span> <span class="kt">CounterFeature</span><span class="p">()</span> <span class="p">})</span>

<span class="c1">// Preview / test</span>
<span class="kt">CounterView</span><span class="p">(</span><span class="nv">store</span><span class="p">:</span> <span class="kt">Store</span><span class="p">(</span><span class="nv">initialState</span><span class="p">:</span> <span class="o">.</span><span class="nf">init</span><span class="p">())</span> <span class="p">{</span> <span class="kt">PreviewCounterFeature</span><span class="p">()</span> <span class="p">})</span>
</code></pre></div></div>

<p>The view has no knowledge of which reducer it received. The generic constraint guarantees both reducers speak the same <code class="language-plaintext highlighter-rouge">State</code> and <code class="language-plaintext highlighter-rouge">Action</code>, so the view compiles against either without change.</p>

<p><strong>Takeaway:</strong> make a TCA view generic over <code class="language-plaintext highlighter-rouge">R: Reducer</code> with <code class="language-plaintext highlighter-rouge">where</code> constraints on <code class="language-plaintext highlighter-rouge">State</code> and <code class="language-plaintext highlighter-rouge">Action</code> to decouple the view from any specific reducer implementation.</p>]]></content><author><name></name></author><category term="devlog" /><category term="swift" /><category term="tca" /><category term="ios" /><category term="architecture" /><summary type="html"><![CDATA[Sometimes the same UI needs different logic depending on context — a form that fetches data in production but returns stubs in previews, or a screen that behaves differently in two flows. In TCA you can handle this by making the view generic over its reducer.]]></summary></entry></feed>