<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Those Who Swift]]></title><description><![CDATA[Your weekly Swift news delivered directly to your inbox!]]></description><link>https://thosewhoswift.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!FQA8!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png</url><title>Those Who Swift</title><link>https://thosewhoswift.substack.com</link></image><generator>Substack</generator><lastBuildDate>Thu, 08 Oct 2026 12:59:51 GMT</lastBuildDate><atom:link href="https://thosewhoswift.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Those Who Swift]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[thosewhoswift@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[thosewhoswift@substack.com]]></itunes:email><itunes:name><![CDATA[Those Who Swift]]></itunes:name></itunes:owner><itunes:author><![CDATA[Those Who Swift]]></itunes:author><googleplay:owner><![CDATA[thosewhoswift@substack.com]]></googleplay:owner><googleplay:email><![CDATA[thosewhoswift@substack.com]]></googleplay:email><googleplay:author><![CDATA[Those Who Swift]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Those Who Swift - Issue 287]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-287</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-287</guid><pubDate>Wed, 07 Oct 2026 19:46:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>Every release process has a ritual that lives in exactly one person&#8217;s fingers- the archive dance, the signing shuffle, the &#8220;oh, you have to flip that switch first.&#8221;</p><p>Lately, developers have been pulling those rituals into the open and handing them to scripts, command lines, and AI agents that don&#8217;t skip steps.</p><p><strong>Which ritual would fall apart if its keeper took a week off?</strong></p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210653241?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h1>If an Agent Can't Read It, It Isn't a Tool Yet</h1><p><em>with Divya Ravi &#8212; Technical Editor</em></p><p>Twelve out of twenty. Then nineteen out of twenty.</p><p>That&#8217;s the number I keep coming back to from Antoine van der Lee&#8217;s RocketTrace benchmark. An AI agent given raw Instruments data found 12 real performance problems. Give it the same recording as a short list of ranked findings, and it found 19, using about a sixth of the tokens.</p><p>Same model. Same app. Same bugs.</p><p>The difference was how the tool presented the output.</p><p>We&#8217;ve spent years building Apple&#8217;s developer tools to be <em>looked at</em>. Instruments is gorgeous. SF Symbols is gorgeous. Xcode&#8217;s build log is, to be kind, a very long document.</p><p>That&#8217;s fine when the reader is a person.</p><p>It matters a lot more when the reader is an agent, a CI runner, or the new hire who wasn&#8217;t there when someone showed you the trick.</p><p>The four pieces in this issue make the same move from different directions.</p><p>Artem Novichkov covers the SF Symbols 27 command-line tool: search, check against your deployment target, export, and let an agent confirm a symbol exists <em>before</em> it writes code using it.</p><p>Daniel Saidi runs Xcode&#8217;s output through <code>xcsift</code>, so Claude Code sees errors, warnings, and test results instead of eleven thousand lines of compiler chatter.</p><p>Jeroen Leenarts swaps a stack of third-party build tools for plain Swift scripts that run in Xcode and Xcode Cloud.</p><p>And RocketTrace turns a trace into findings.</p><p>Different tools, same idea:</p><p><strong>Cut the output down to the part that matters.</strong></p><p>I don&#8217;t think everything should become a CLI. A GUI is exactly right when you&#8217;re exploring. Nobody wants to see their first flame graph as JSON.</p><p>The point is narrower: once you understand a problem, the useful output should be something you can hand to a script, a pipeline, or a model without a person translating in the middle.</p><p><strong>Exploring needs pixels. Repeating needs text.</strong></p><p>There&#8217;s another reason this matters, and Apple made it pretty clear this week.</p><p>The Full Disk Access update points out the risk of giving autonomous agents broad access to a Mac. Jeff Johnson&#8217;s look at Meta Muse&#8217;s first-run experience makes the same point from the user&#8217;s side.</p><p>The more we give agents, the more it matters what a tool exposes versus what an agent can reach.</p><p>A narrow interface &#8212; check this symbol, summarize this trace, filter this build &#8212; saves tokens, but it also shrinks the blast radius.</p><p>So here&#8217;s the habit I&#8217;d steal: next time you&#8217;re walking a colleague or a model through a manual step, stop and ask what the smallest useful <em>output</em> is, and whether the tool can produce it itself.</p><p>If it can&#8217;t, you&#8217;ve probably found your next side project.</p><p><strong>Which step in your release process exists only in somebody&#8217;s head? Hit reply and tell me.</strong></p><div><hr></div><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><h3><a href="https://www.avanderlee.com/ai-development/introducing-rockettrace-using-xcode-instruments-with-ai-agents/">Introducing RocketTrace: Using Xcode Instruments with AI Agents</a></h3><p>Antoine van der Lee&#8217;s new Mac app analyzes Instruments recordings locally and gives AI agents ranked findings instead of raw traces. In his benchmark, agents found 19 of 20 real issues versus 12 without it, using about a sixth of the tokens.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://artemnovichkov.com/blog/sf-symbols-from-the-command-line-and-ai-agents">SF Symbols from the Command Line and AI Agents</a></h3><p>Artem Novichkov covers SF Symbols 27's command-line tool: search, check availability against your deployment target, export, and let agents validate symbol names before generating code that references one that doesn't exist.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://danielsaidi.com/blog/2026/09/30/optimizing-claude-code-s-xcode-build-token-usage">Optimizing Claude Code's Xcode build token usage</a></h3><p>Daniel Saidi adds a hook that pipes Xcode build output through <code>xcsift</code>, keeping only errors, warnings, and test results so your agent stops burning tokens on build noise.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://appforce1.net/blog/swift-scripting-in-xcode">Swift scripts for Xcode and Xcode Cloud</a></h3><p>Jeroen Leenarts replaces external build tooling with Swift scripts that run in both Xcode and Xcode Cloud using the compiler you already have: version management, icon badging, translation validation, and release notes.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://massicotte.org/blog/revisiting-split-isolation/">Revisiting "Split Isolation"</a></h3><p>Matt Massicotte takes another look at a pattern he once named and warned against, where a non-Sendable type has some main-actor methods and some not. Still usually a problem, but defensible during migrations or for secondary functionality.</p><p><strong><span>&#128205;Level: </span>Advanced</strong></p><h3><a href="https://www.swiftdifferently.com/blog/swift/concurrency/with-task-cancellation-shield">withTaskCancellationShield: Swift 6.4 new feature</a></h3><p>Omar Elsayed explains Swift 6.4's <code>withTaskCancellationShield</code>, which lets code finish even when its task is cancelled, but only on OS 27 and later. His workaround for older OS versions is a neat lesson in how cancellation propagates.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://fatbobman.com/en/weekly/issue-156">Swift Server Has Another Google</a></h3><p>Google Cloud ships a server-side Swift SDK covering 100+ services. Fatbobman's measured take: a real milestone for the ecosystem, aimed at containers and DevOps rather than iOS apps, but not a revolution.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187;</h1><h3><a href="https://aleahim.com/blog/swiftui-modifier-order/">Modifier Order Is Matrix Order</a></h3><p>Mihaela shows that SwiftUI transform modifiers compose like matrix multiplication, opposite to how Core Graphics reads, and that <code>.offset</code> quietly snaps to the device pixel grid first. Confirmed with pixel-level measurement, not guesswork.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://sarunw.com/posts/toolbar-overflow-and-visibility-priority-on-iphone-duo/">Toolbar overflow and visibility priority on iPhone Duo</a></h3><p>Sarun Wongpatcharapakorn covers how to choose between toolbar and tab bar on the Duo's vertical bars, consolidate overflow, and set visibility priority so essential actions survive tight space.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://adincebic.com/2026/10/04/configuring-bazel-module-extensions-with.html">Configuring Bazel Module Extensions with tag_class</a></h3><p>Adin Cebic shows how Bazel&#8217;s <code>tag_class</code> lets users configure module extensions from <code>MODULE.bazel</code>, with a worked binary-download extension. One for teams who&#8217;ve outgrown Xcode-project-only builds.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/news/?id=kkphp5qo">Prepare and submit your apps for iPhone Duo</a></h3><p>iPhone Duo reaches customers October 23. Build with Xcode 27.1 now; from April 2027, newly submitted apps must include Duo screenshots.</p><h3><a href="https://developer.apple.com/news/?id=p6zjojqw">Updates to Full Disk Access in macOS</a></h3><p>Apple is adding controls so users must take explicit action before granting Full Disk Access, citing the growing risk from autonomous AI agents. Relevant if your Mac app or tool asks for it.</p><h3><a href="https://developer.apple.com/news/?id=ljpl7kyn">Showcase your apps with new assets on the App Store</a></h3><p>New product-page header and search-result assets, an App Store Connect preview across devices and Dark Mode, and an Asset Library for scheduling seasonal creative.</p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://ikennd.ac/blog/2026/10/gen-ai-thoughts/">My Thoughts on Generative AI: October 2026]</a></h3><p>Daniel Kennett, tired of how polarized AI talk is, encrypts his actual opinion and promises to publish the key in a year so it can be judged in hindsight. A refreshingly different answer to the discourse.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><h1 style="text-align: center;">Design&#127912;</h1><h3><a href="https://tanaschita.com/ios-accessibility-dynamic-type-layouts/">Adapting SwiftUI layouts for large Dynamic Type sizes</a></h3><p>Natascha Fadeeva pairs <code>dynamicTypeSize.isAccessibilitySize</code> with <code>AnyLayout</code> to flip a row from horizontal to vertical at accessibility sizes while preserving child view identity and state.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Community &#129309; </h1><h3><a href="https://lapcatsoftware.com/articles/2026/10/2.html">Meta Muse Mac first run experience</a></h3><p>Jeff Johnson documents Muse&#8217;s first launch on macOS, showing it needs explicit Full Disk Access authorization before reading Messages, and rebuts a journalist&#8217;s claim of unauthorized access. A good companion to Apple&#8217;s Full Disk Access notice.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://sebastianroehl.substack.com/p/building-an-indie-app-business-115">Building An Indie App Business</a></h3><p>Sebastian R&#246;hl's weekly indie log: syncing HabitKit across iOS and Android, automatic iCloud backups, deleting legacy database code, and juggling several AI-assisted projects at once.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Devs for Devs: You Are Valuable, Act Like It.]]></title><description><![CDATA[From world-class developers, straight to you.]]></description><link>https://thosewhoswift.substack.com/p/devs-for-devs-you-are-valuable-act</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/devs-for-devs-you-are-valuable-act</guid><pubDate>Sun, 04 Oct 2026 20:18:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello! &#128075;</p><p>We&#8217;re happy to welcome Manuel Cazalla Colmenero as our guest author.</p><p>Manuel is an iOS developer working with Swift, SwiftUI, and UIKit, with hands-on experience building and maintaining production applications.</p><p>Beyond his professional work, Manuel is also an indie developer who has taken two of his own apps all the way from idea to App Store. That journey has given him practical experience beyond coding, from architecture and persistence to testing, localization, monetization, App Store review, and actually shipping a product to users.</p><p>P.S. You can follow Manuel and his iOS development journey on &#8288;<a href="https://es.linkedin.com/in/manuelcazalla">LinkedIn</a> and <a href="https://manuelcazalla.github.io/ManuelCAZALLA/">GitHub</a>.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xNNw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!xNNw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!xNNw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!xNNw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xNNw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:292575,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/218466460?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xNNw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!xNNw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!xNNw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!xNNw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F906c28cb-2582-4024-ad1f-ecff4a6e9b0b_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><p>Hey! &#128075;</p><p>Some of you know me, but for those who don&#8217;t, I&#8217;m Jacob, a husband and father of 5 kids under 10 years old, whom we homeschool. One of our kids has a rare medical condition that consumes a lot of our time, and it&#8217;s unfortunately not uncommon for us to spend weeks or months in the hospital with him.</p><p>We live out in the sticks in Wisconsin, trying to farm a bit. We&#8217;re better with animals than plants so far. We&#8217;ve had sheep, goats, and currently lots of chickens. We rest on Saturdays, spend our days at church on Sundays, and I run a Bible study on Monday nights that usually lasts until midnight.</p><p>Aside from all that, I spend much of my time helping developers make more money: I&#8217;m working on the SDKs at RevenueCat full-time.</p><p>So&#8230; I&#8217;m what you <em>might</em> call a &#8220;busy&#8221; guy.</p><p>Regardless, this year I've shipped 2 iOS apps (<a href="https://apps.apple.com/us/app/noah-weather/id6792880452">Noah Weather</a> &amp; <a href="https://apps.apple.com/us/app/whoya/id6757703773">Whoya</a>) and 1 SaaS tool (<a href="https://thebnbfactory.com/">BnbPhotoFactory</a>). All are good-quality apps, and one is grossing a few thousand dollars a month.</p><p>You may wonder how I have the time to build. As you could probably guess, I don&#8217;t have much time. I have to squeeze it in, usually when I&#8217;d rather be sleeping. And many times, it&#8217;s really easy to put off working on my own apps and just get some sleep&#8230; A lot of times I do.</p><p>AI has certainly helped with velocity, but it&#8217;s not a silver bullet. There are things that require my mind and my time. I don&#8217;t want to ship slop. I&#8217;m still a craftsman, and that means I need to make time.</p><p>So how do you do that? How do you make the time?</p><p>I&#8217;m going to answer that, maybe in a more roundabout way than is typical. To give you some breadcrumbs, I want you to take away 3 things from this article:</p><ol><li><p>Know your value</p></li><li><p>Cut out the noise</p></li><li><p>Make it easy to commit to</p></li></ol><h2>The Battle of the Mind</h2><p>As a parent, time seems to fly by. My oldest is halfway to adulthood, but it feels like a month ago I was rocking her to sleep on my shoulder and changing her diapers all the time. And now, having a child with a severe medical condition, I realize how precious life is. How precious my time is. Life is not like Prince of Persia or Click, where I could rewind and undo things. I can&#8217;t go backwards and change or relive anything. When I trade my time, it&#8217;s a permanent trade. That time is gone. Forever.</p><p>So&#8230; What am I willing to trade that time for? Not much, honestly.</p><p>We cut a lot out of our lives.</p><p>We don&#8217;t watch TV or play video games anymore. We&#8217;re not on most social media platforms. We intentionally avoid the fearmongering media outlets. We aren&#8217;t willingly being ignorant, we pick and choose how and where we consume our information. We don&#8217;t allow those kinds of distractions into our lives, because we don&#8217;t want to waste our time.</p><p>We also cut out consumer debt. We spent years paying off the loans and credit cards, and cut them up. The payments on the debt nagged at my thoughts. They were stealing my peace and made it harder to be flexible with our lives. So we cut that out too. We don&#8217;t even have the desire to spend the mental energy playing the game for points. I don&#8217;t care about a few hundred extra bucks a year. The cost, my mental energy, is not worth it.</p><p>We are trying to have free minds and therefore free lives.</p><p>To get there, we had to choose that our lives were more valuable than the TV and the games. That peace of mind was more valuable than having the newest things and a credit card statement.</p><p>You may not need to go to the same lengths as we did. But what you certainly need to do is make it easy to choose. You have to do the work up front to know how valuable your life is and what your values are. It becomes easy to choose how to trade your time when you know your value. But if you don&#8217;t know the value of your time, it&#8217;s easy to allow all sorts of things to fill it.</p><h2>How I Choose</h2><p>When you&#8217;re as busy as I am, you don&#8217;t have much time to trade on projects. At that point, it can be discouraging. You have all kinds of ideas! And with AI, it&#8217;s a lot easier to make them reality! But you still have to prioritize what is most important. And that means, at least for me, there&#8217;s not much time to build.</p><p>If I choose to work on an idea that has potential but I&#8217;m not that fired up about it, I have a hard time committing my time to it. I also struggle to balance more than one idea at once. This is especially true if I&#8217;m excited about all of the ideas I&#8217;m trying to work on simultaneously.</p><p>So what I do is focus on just one app at a time. If there&#8217;s one that&#8217;s clearly urgent, like there&#8217;s money on the table, that one wins. But other than that, it&#8217;s whatever I&#8217;m most excited about. Apps that I want to build for ME or my family. It&#8217;s not easy to trade sleep for some random money-making idea. But it is SO EASY to trade it for an app I&#8217;m passionate about, because money is just a nice bonus.</p><p>If you&#8217;re married (or dating), I would also choose an idea your other half is interested in. Showing off what you worked on to an unimpressed audience is a total momentum killer. But showing off an app you&#8217;re both excited about fuels the flames. It works with friends too, but it&#8217;s not the same.</p><p>I also generally like to build for me first. When an app idea is solid enough that I think others would want it, I test that theory. I get it polished a bit, push it to TestFlight, invite some people, and see if it&#8217;s worth pursuing. If there&#8217;s enough interest, I&#8217;ll commit to the v1 feature set and polish, and go from there.</p><h2>Wrapping It Up</h2><p>So, if you&#8217;re anything like me and you don&#8217;t have much time to spend on things, here&#8217;s my advice. Take stock of what you spend your time on and evaluate whether it matches your values. Does the way you spend your time show that you value your faith, yourself, and your family? Cut out anything that competes with your values. Do the hard work now, because it&#8217;s so much easier to walk and run when you don&#8217;t have weights around your ankles. Then build from a place of excitement and passion! It&#8217;s a fun way to be.</p><h3>My Apps</h3><p>In the introduction, I mentioned things I&#8217;ve shipped this year outside of work. They are:</p><ul><li><p><a href="https://apps.apple.com/us/app/whoya/id6757703773">Whoya</a> - a phone number memorization game I made for my kids.</p></li><li><p><a href="https://apps.apple.com/us/app/noah-weather/id6792880452">Noah Weather</a> - my most recent app: an encouraging weather app for Christians.</p></li><li><p><a href="https://thebnbfactory.com/">BnbPhotoFactory&#8217;s</a> - AI image editing pipeline SaaS tool.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075; Every Wednesday news, every Sunday tips from devs!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 286]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-286</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-286</guid><pubDate>Wed, 30 Sep 2026 20:30:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>The iPhone Duo simulator landed in Xcode 27.1 beta, and suddenly a lot of familiar SwiftUI layout assumptions are getting tested in the same week.</p><p>The interesting one? <strong>Centered doesn&#8217;t always mean centered anymore.</strong></p><p>Have you opened the Duo simulator yet, or are you waiting to see whether this actually becomes a real device?</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210653241?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h1><strong>Your Centered Layout Was Never as Stable as You Though</strong>t</h1><p><em>with Divya Ravi, Technical Editor</em></p><p>Four people picked up the same beta this week and came at it from completely different directions.</p><p>Blake Crosley looked at what actually shipped in Xcode 27.1, the two-display simulator, the new Duo APIs, and the things that still aren&#8217;t there. Antoine van der Lee put a real SwiftUI layout through the simulator. Mick MacCallum went deep on ArrangementView, including its split and overlay modes. And Xiangyu Sun wrote the piece that stuck with me most: centered layouts, the kind we&#8217;ve been shipping for years without thinking twice, stop being centered the moment the device folds.</p><p>That&#8217;s the interesting part. Not the device itself.</p><p>A foldable phone isn&#8217;t conceptually that far from an iPad in Split View. Content gets squeezed, resized, or divided into separate regions. We&#8217;ve been dealing with versions of this problem for years.</p><p>The difference with Duo is that the failure becomes painfully literal.</p><p>On an iPad, a bad assumption about available width might leave you with an ugly but usable layout. On Duo, a bad assumption about &#8220;the center of the screen&#8221; can put a button across a physical seam or leave a modal split between two panels.</p><p>That&#8217;s not a subtle bug. It&#8217;s the kind of thing a designer can spot walking past your desk before QA even files the ticket.</p><p>What I keep coming back to after reading all four pieces is that they&#8217;re really describing the same discipline from different angles.</p><p>Crosley&#8217;s piece is about reading a beta carefully, understanding what you can actually test instead of assuming feature parity because an API exists.</p><p>Van der Lee&#8217;s is about treating new platform APIs as an enhancement to resilient layout code, not a substitute for it.</p><p>MacCallum&#8217;s is about making split and overlay behavior explicit instead of hoping GeometryReader will somehow figure it out.</p><p>And Sun&#8217;s piece gets to the underlying problem: &#8220;reserved regions&#8221; is essentially Apple&#8217;s way of acknowledging that a single content plane was always a convenient simplification. Now there&#8217;s a name for the places where that simplification breaks down.</p><p>None of this is really about foldables.</p><p>&#8220;The screen&#8221; was never actually one thing. It&#8217;s just an abstraction we&#8217;ve gotten comfortable with.</p><p>iPad multitasking chipped away at it. Dynamic Island chipped away at it again. Stage Manager did the same. Duo just makes the boundary physical.</p><p>And every time a new form factor arrives, the same two kinds of codebases show up.</p><p>There are the ones built around constraints and adaptive containers, where supporting the new shape is mostly a matter of wiring things up.</p><p>Then there are the ones where the assumption of a fixed rectangle is buried so deeply that &#8220;support the new screen&#8221; turns into rewriting half the view hierarchy under a deadline.</p><p>You don&#8217;t need a Duo roadmap item this quarter to take something from this.</p><p>The more useful question is much smaller:</p><p><strong>Where in your codebase do you assume there is exactly one visible region, one center point, one continuous canvas?</strong></p><p>Not because Duo will necessarily expose it.</p><p>It might never.</p><p>But the assumption was already fragile. Every new form factor Apple ships is just another way of finding out where we&#8217;ve made that assumption.</p><p><strong>Hit reply and tell me: </strong>have you actually opened the Duo simulator yet, or are you in the &#8220;let&#8217;s see if this becomes a real SKU first&#8221; camp?</p><p>I&#8217;m genuinely curious which way you&#8217;re leaning, especially this close to launch.</p><div><hr></div><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><p style="text-align: center;">Four people picked up the same beta this week and came at it from completely different directions.</p><h3><a href="https://blakecrosley.com/blog/xcode-27-1-beta-iphone-duo-simulator">Xcode 27.1 Beta: Your App in the iPhone Duo Simulator</a></h3><p>Blake Crosley walks through what actually shipped in the 27.1 beta, the two-display simulator (466&#215;678 outer, 669&#215;951 inner), the new Duo APIs, and what&#8217;s still missing, including StandBy simulation.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.avanderlee.com/swiftui/iphone-duo-simulator/">iPhone Duo Simulator: Testing and optimizing your SwiftUI app</a></h3><p>Antoine van der Lee puts ArrangementView and reserved regions through a practical SwiftUI walkthrough. The key takeaway: make your layout resilient first, then treat Duo-specific APIs as an enhancement.</p><p><strong>&#128205;Level: </strong>Intermediate</p><h3><a href="https://bleepingswift.com/blog/arrangementview-swiftui-iphone-duo">Building Fold-Aware Layouts with ArrangementView in SwiftUI</a></h3><p>Mick MacCallum goes deeper into ArrangementView&#8217;s split and overlay styles, sizing and axis configuration, along with its UIKit counterpart, UIArrangementViewController.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://medium.com/ios-ic-weekly/iphone-duo-breaks-centered-layouts-reserved-regions-displacement-and-arrangementview-4efae455f9b6">iPhone Duo Breaks Centered Layouts: Reserved Regions, Displacement and ArrangementView</a></h3><p>Xiangyu Sun looks at what happens when the Duo is folded and a layout&#8217;s idea of &#8220;center&#8221; no longer matches the physical screen. His discussion of reserved regions is a useful way to think about layouts that were built around a single continuous surface.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://www.swiftbysundell.com/articles/using-swifts-some-keyword-beyond-swiftui">Using Swift's 'some' keyword beyond SwiftUI</a></h3><p>John Sundell looks past the SwiftUI use case to show how `some` simplifies function parameters, avoids type erasure, and builds cleaner genericAPIs, all while keeping compile-time type resolution.</p><p><strong><span>&#128205;Level: </span>Intermediate</strong></p><h3><a href="https://forums.swift.org/t/se-0554-deployment-target-conditional-compilation/89822">Deployment Target Conditional Compilation</a></h3><p>A new Swift Evolution proposal, now in review, that lets code adapt directly to the minimum deployment target instead of relying on runtime <code>#avialable</code> checks alone, a cleaner path for apps supporting multiple OS versions.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://forums.swift.org/t/se-0553-task-identity/89728">Task Identity</a></h3><p>A proposal for a stable `Task.ID` type, so concurrent tasks can be correlated across logs and tracing tools without the overhead of wrapping them just to get an identifier, currently in review, with real debate over misuse-as-a-side-channel concerns.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187; </h1><h3><a href="https://raska.io/blog/testing-concurrent-code">Stop Sleeping: Deterministic Tests for Concurrent Swift Code</a></h3><p>Michal Ra&#353;ka shows how to use a &#8220;test spy&#8221; to make unstructured concurrent Swift code deterministic without sprinkling sleep() through your tests. A practical solution to a very real Swift 6 testing problem.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://salari.dev/writing/what-minimum-scale-factor-trades-away-in-swiftui/">What minimumScaleFactor trades away in SwiftUI</a></h3><p>Mike Salari runs 500 measurements across 100 conditions to show how minimumScaleFactor can quietly undermine visual hierarchy when neighboring labels scale independently. </p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://link.springer.com/book/10.1007/979-8-8688-2815-7">Real-World iOS Development</a></h3><p>Avi Tsadok's new book (Apress/Springer) on the practices real teams actually use in production, architecture and design patterns, testing, CI/CD, navigation, data persistence, team workflows, and a closing chapter on adapting to the AI era.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/news/?id=nyuppv9r">Build for iPhone Duo with new resources</a></h3><p>Apple&#8217;s official Duo resources, covering the Xcode 27.1 beta, design kits, and optimization workshops, the primary source behind much of this week&#8217;s Duo coverage.</p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://sergdort.github.io/xcode-agent-incremental-builds/">Making Xcode and agents share incremental builds</a></h3><p>Serg Dort tackles a surprisingly annoying workflow problem: switching between Xcode and AI coding agents can throw away incremental build work. This shows how to get both workflows sharing the same build outputs.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://www.natashatherobot.com/p/this-week-in-swift-x-ai-sep25">This Week in Swift x AI</a></h3><p>Natasha The Robot rounds up recent Swift and iOS AI developments, from SwiftFairy&#8217;s launch and JEV decision models to what &#8220;in-app computer use on iPhone Duo&#8221; could mean for developers</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Design &#127912; </h1><h3><a href="https://amerpie.lol/2026/09/08/mckinley-the-sf-symbols-editor.html">McKinley: The SF Symbols Editor Apple Never Built</a></h3><p>Lou Plummer gives McKinley, a new Mac app for generating and previewing SF Symbols weights and scales, a hands-on test using Apple&#8217;s own rendering engine. The review covers both what works and where the app still falls short.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><h1 style="text-align: center;">Tutorials&#128210;</h1><h3><a href="https://serialcoder.dev/swiftui/swipe-actions-beyond-lists-in-swiftui/">Swipe Actions Beyond Lists in SwiftUI</a></h3><p>Gabriel Theodoropoulos walks through swipeActionsContainer, showing how to bring swipe actions to grid layouts rather than limiting them to lists. Clear, practical, and a welcome return after a long rest.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://antongubarenko.substack.com/p/swiftui-charts-dynamic-masking">SwiftUI Charts: Dynamic Masking</a></h3><p>Anton Gubarenko builds a TradingView-style chart where the selected range stays at full opacity while the rest fades back, using layered Swift Charts masking instead of rebuilding the dataset.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Video &#127909;</h1><h3><a href="https://www.youtube.com/watch?v=Dt4o7hz0B60">This Filename Breaks iOS, And That's Boring Now</a></h3><p>Bryce Bostwick digs into a real iOS rendering bug involving filenames such as &#8220;highjump.png&#8221; and Apple&#8217;s IBHashImageName function, then turns it into a broader question: what happens to deep technical writeups when an LLM can solve the bug for you?</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Community &#129309;</h1><h3><a href="https://ivanthinking.net/2026/09/22/little-memory-2.0-moving-15-years-of-memories-off-my-servers/">Little Memory 2.0: Moving 15 years of memories off my servers</a></h3><p>Ivan Tse rebuilt his decade-old journaling app around offline-first storage and iCloud sync, moving sensitive diary data off his own servers. A thoughtful indie-dev story about architecture, privacy, and what happens when you rethink an old product.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://hackernoon.com/shipaton-at-week-six-buildhorn-has-officially-been-submitted-to-apple">Shipaton at Week Six: Buildhorn Has Officially Been Submitted to Apple</a></h3><p>The latest entry in Darryl Bayliss&#8217;s Shipaton devlog follows Buildhorn through App Store submission, including a performance issue caused by AI-generated code in widget syncing.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Devs for Devs: Opening In-App Search from Siri with App Intents in iOS 27]]></title><description><![CDATA[From world-class developers, straight to you.]]></description><link>https://thosewhoswift.substack.com/p/devs-for-devs-opening-in-app-search</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/devs-for-devs-opening-in-app-search</guid><pubDate>Sun, 27 Sep 2026 20:01:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello! &#128075;</p><p>Today, I&#8217;m happy to say we have Ildar Zalyalov as our guest author. He&#8217;s a Lead Software Engineer at H&amp;M Group with more than 11 years of professional experience in iOS development.</p><p>His journey spans the evolution from Objective-C to Swift, SwiftUI, modern concurrency, and AI-assisted development. Beyond building products, Ildar actively supports the iOS community through teaching and mock interviews, helping developers strengthen both their technical knowledge and interview skills.</p><p>P.S. You can check out Ildar&#8217;s work on <strong><a href="https://www.linkedin.com/in/ildar-zalyalov-ios/">LinkedIn</a> </strong>and <strong><a href="https://www.youtube.com/@iOSwithIZ">YouTube</a></strong>.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4OL6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!4OL6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!4OL6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!4OL6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4OL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:324574,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/216211580?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4OL6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!4OL6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!4OL6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!4OL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71c1e83a-7302-4375-a64f-0becce9a40a3_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075;</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p>A schema gives an intent meaning that the system already understands. Instead of exposing an arbitrary action that happens to accept a search string, for example, we can describe it as an actual search operation.</p><p>In this post, we&#8217;ll look at why system schemas are useful, and implement an in-app search intent that can open an app directly on its own search results.</p><h2>Prefer a System Schema When One Fits</h2><p>Suppose we&#8217;re building a recipe app and want to expose its search functionality to Siri.</p><p>We could start with a custom App Intent:</p><pre><code><code>import AppIntents

struct SearchRecipesIntent: AppIntent {
    static let title: LocalizedStringResource = "Search Recipes"

    @Parameter(title: "Query")
    var query: String

    func perform() async throws -&gt; some IntentResult {
        // Pass the query to the app.
        return .result()
    }
}</code></code></pre><p>This is a valid intent. The system sees an action called <code>Search Recipes</code> with a string parameter.</p><p>But search isn&#8217;t really specific to our app. It&#8217;s an operation Siri already understands.</p><p>App Schemas describe common actions using structures known to the system. Apple provides schemas across domains such as messages, mail, photos, media, and system functionality.</p><p>When one accurately represents what our app does, it&#8217;s usually better to adopt that schema than to create another custom representation of the same operation.</p><p>For in-app search, we can use the system search schema:</p><pre><code><code>import AppIntents

@available(iOS 27, *)
@AppIntent(schema: .system.searchInApp)
struct SearchRecipesIntent: ShowInAppSearchResultsIntent {
    static var supportedModes: IntentModes { .foreground }

    static var allowedExecutionTargets: IntentExecutionTargets {
        .main
    }

    static let searchScopes: [StringSearchScope] = [
        .general
    ]

    @Parameter
    var criteria: StringSearchCriteria

    func perform() async throws -&gt; some IntentResult {
        let query = criteria.term

        // Open the app's search experience.
        return .result()
    }
}</code></code></pre><p>The difference is more than syntax.</p><p>With the custom intent, <code>query</code> is simply a parameter we defined. With <code>ShowInAppSearchResultsIntent</code>, the intent adopts the system&#8217;s search contract. The system provides <code>StringSearchCriteria</code>, and its <code>term</code> property contains the string to search for.</p><p>Apple recommends <code>StringSearchCriteria</code> for ordinary text-based searches, together with the <code>.general</code> search scope for general app content.</p><p><code>StringSearchScope.general</code> also tells the system that the app provides general search functionality. Apple notes that this may participate in system-wide universal search behavior, depending on the app and its content.</p><h2>Running the Intent in the App</h2><p>A search intent is unusual in one important way: performing the action isn&#8217;t enough. We want to show something in our interface.</p><p><code>ShowInAppSearchResultsIntent</code> is specifically intended to display search results in the app. Apple also notes that this kind of intent needs to run from the app rather than an App Intents extension.</p><p>For that reason, I prefer making the execution requirements explicit:</p><pre><code><code>static var supportedModes: IntentModes {
    .foreground
}

static var allowedExecutionTargets: IntentExecutionTargets {
    .main
}</code></code></pre><p><code>ShowInAppSearchResultsIntent</code> already provides a foreground-capable default, so declaring <code>supportedModes</code> isn&#8217;t strictly necessary in every implementation.</p><p>I still find it useful here because it documents an important property of the intent: its result is an app UI transition, not a background operation.</p><p>Restricting the execution target to <code>.main</code> is more significant when App Intents code lives in a shared framework or the application also has App Intents or Widget extensions. It makes sure this particular action executes in the main app process.</p><h2>Don&#8217;t Assume the Term is Useful</h2><p><code>StringSearchCriteria.term</code> is a non-optional string, but that doesn&#8217;t necessarily mean it contains a useful search query.</p><p>A small validation step makes the intent more robust:</p><pre><code><code>static func normalizedTerm(_ term: String) -&gt; String? {
    let trimmed = term.trimmingCharacters(
        in: .whitespacesAndNewlines
    )

    return trimmed.isEmpty ? nil : trimmed
}</code></code></pre><p>We can use it from <code>perform()</code>:</p><pre><code><code>@MainActor
func perform() async throws -&gt; some IntentResult {
    guard let searchTerm = Self.normalizedTerm(criteria.term) else {
        throw $criteria.needsValueError(
            "What do you want to search for?"
        )
    }

    // Open search for `searchTerm`.

    return .result()
}</code></code></pre><p>Throwing <code>needsValueError</code> is more useful than silently doing nothing. It tells the App Intents system that the parameter doesn&#8217;t contain enough information to continue and gives it a chance to ask the user for a value.</p><p>For a request such as: &#8220;Show me pasta recipes in RecipeBox.&#8221; the value we eventually care about is just:</p><pre><code><code>"pasta"</code></code></pre><p>The interesting part is what we do with it next.</p><h2>Hand the Search Off to the App</h2><p>A small demo could navigate directly from <code>perform()</code>.</p><p>In a real application, that becomes fragile quickly.</p><p>The intent may run while the application is already visible, or it may cause a cold launch. In the second case, <code>perform()</code>can happen before the root view hierarchy is ready to navigate.</p><p>Instead of making the intent know about view controllers or SwiftUI navigation, we can hand the action over to application-level routing code.</p><p>Here&#8217;s a small version of that pattern:</p><pre><code><code>import Observation

enum Destination: Hashable {
    case search(String)
}

@MainActor
@Observable
final class AppNavigation {
    var path: [Destination] = []

    private var pendingSearch: String?
    private var isUIReady = false

    func enqueueSearch(_ query: String) {
        pendingSearch = query
        replayPendingSearchIfPossible()
    }

    func markUIReady() {
        isUIReady = true
        replayPendingSearchIfPossible()
    }

    private func replayPendingSearchIfPossible() {
        guard
            isUIReady,
            let query = pendingSearch
        else {
            return
        }

        pendingSearch = nil
        path.append(.search(query))
    }
}</code></code></pre><p>The distinction between <code>enqueueSearch()</code> and actually changing the navigation path is intentional.</p><p>If the UI already exists, the queued search is handled immediately.</p><p>If the app is still launching, the request remains pending until the UI reports that it&#8217;s ready.</p><p>The intent itself stays small:</p><pre><code><code>import AppIntents

@available(iOS 27, *)
@AppIntent(schema: .system.searchInApp)
struct SearchRecipesIntent: ShowInAppSearchResultsIntent {
    static var supportedModes: IntentModes { .foreground }

    static var allowedExecutionTargets: IntentExecutionTargets {
        .main
    }

    static let searchScopes: [StringSearchScope] = [
        .general
    ]

    @Parameter
    var criteria: StringSearchCriteria

    @Dependency
    private var navigation: AppNavigation

    @MainActor
    func perform() async throws -&gt; some IntentResult {
        guard let searchTerm = Self.normalizedTerm(criteria.term) else {
            throw $criteria.needsValueError(
                "What do you want to search for?"
            )
        }

        navigation.enqueueSearch(searchTerm)

        return .result()
    }

    static func normalizedTerm(_ term: String) -&gt; String? {
        let trimmed = term.trimmingCharacters(
            in: .whitespacesAndNewlines
        )

        return trimmed.isEmpty ? nil : trimmed
    }
}</code></code></pre><p>Notice what isn&#8217;t here.</p><p>The intent doesn&#8217;t push a view controller, mutate a tab bar, construct a SwiftUI destination, or perform the search itself.</p><p>It only translates a system intent into an application action.</p><h2>Handling Warm and Cold Launches</h2><p>We can register the same navigation object with App Intents and SwiftUI:</p><pre><code><code>import AppIntents
import SwiftUI

@main
struct RecipeBoxApp: App {
    @State private var navigation: AppNavigation

    init() {
        let navigation = AppNavigation()

        _navigation = State(initialValue: navigation)

        AppDependencyManager.shared.add(
            dependency: navigation
        )
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(navigation)
        }
    }
}
</code></code></pre><p>And let the root UI announce when navigation is available:</p><pre><code><code>import SwiftUI

struct ContentView: View {
    @Environment(AppNavigation.self)
    private var navigation

    var body: some View {
        @Bindable var navigation = navigation

        NavigationStack(path: $navigation.path) {
            List {
                Text("Spaghetti Carbonara")
                Text("Pasta Primavera")
                Text("Tomato Soup")
                Text("Margherita Pizza")
            }
            .navigationTitle("Recipes")
            .navigationDestination(for: Destination.self) { destination in
                switch destination {
                case .search(let query):
                    SearchResultsView(query: query)
                }
            }
        }
        .task {
            navigation.markUIReady()
        }
    }
}</code></code></pre><p>For a warm launch, <code>isUIReady</code> is already true and <code>enqueueSearch()</code> immediately adds the search destination.</p><p>For a cold launch, the intent stores the query first. Once <code>ContentView</code> appears, <code>markUIReady()</code> replays the pending action.</p><p>This mirrors a useful pattern in larger applications, where intents, deep links, push notifications, widgets, and other external entry points all feed the same action or navigation coordinator.</p><p>If an app already has such an abstraction, the intent should use it rather than introducing a new routing system specifically for Siri.</p><h2>Displaying the Results</h2><p>Our result screen doesn&#8217;t need to know where the query came from:</p><pre><code><code>import SwiftUI

struct SearchResultsView: View {
    @State private var query: String

    private let recipes = [
        "Spaghetti Carbonara",
        "Pasta Primavera",
        "Tomato Soup",
        "Margherita Pizza"
    ]

    init(query: String) {
        _query = State(initialValue: query)
    }

    private var results: [String] {
        recipes.filter {
            $0.localizedCaseInsensitiveContains(query)
        }
    }

    var body: some View {
        List(results, id: \.self) { recipe in
            Text(recipe)
        }
        .navigationTitle("Search")
        .searchable(
            text: $query,
            prompt: "Search recipes"
        )
    }
}</code></code></pre><p>The same screen could have been opened from the app itself, from a deep link, or from Siri.</p><p>That&#8217;s a useful boundary.</p><p>Siri provides the meaning of the external request. The intent translates it into an app action. From that point on, the existing application architecture takes over.</p><h2>In-app Search and App Entities Solve Different Problems</h2><p><code>searchInApp</code> doesn&#8217;t replace App Entities.</p><p>An <code>AppEntity</code> represents an identifiable object from our app. A recipe app could expose individual recipes as <code>RecipeEntity</code> values, allowing Siri and other system experiences to resolve or operate on a particular recipe.</p><p>In-app search serves a different purpose.</p><p>Instead of requiring the system to resolve one entity, <code>ShowInAppSearchResultsIntent</code> gives the search criteria back to the app and lets the app display its own results.</p><p>That works particularly well when the search experience includes server-side content, filters, ranking, recommendations, or presentation logic we already maintain ourselves.</p><p>Apple also notes that apps can use this search integration without requiring every searchable entity to be indexed first.</p><h2>Keep System Intents Thin</h2><p>The most useful part of the implementation isn&#8217;t the amount of Siri-specific code we added.</p><p>It&#8217;s how little we needed.</p><p>The final intent essentially does three things:</p><pre><code><code>guard let term = Self.normalizedTerm(criteria.term) else {
    throw $criteria.needsValueError(
        "What do you want to search for?"
    )
}

navigation.enqueueSearch(term)

return .result()</code></code></pre><p>The system schema describes what the action means. <code>StringSearchCriteria</code> carries the query. The application&#8217;s existing routing and search layers decide what happens next.</p><p>This is a good pattern beyond search as well. Before creating a custom App Intent, it&#8217;s worth checking whether an App Schema already describes the operation. If it does, we can use the system&#8217;s vocabulary and keep the intent itself as a small adapter into the application.</p><p>For in-app search, that adapter is enough to take a request from Siri all the way to a native search results screen.</p><h2>References</h2><ul><li><p><a href="https://developer.apple.com/documentation/appintents/showinappsearchresultsintent">ShowInAppSearchResultsIntent</a></p></li><li><p><a href="https://developer.apple.com/documentation/appintents/stringsearchcriteria">StringSearchCriteria</a></p></li><li><p><a href="https://developer.apple.com/documentation/appintents/stringsearchscope/general">StringSearchScope.general</a></p></li><li><p><a href="https://developer.apple.com/documentation/appintents/appintent/allowedexecutiontargets">allowedExecutionTargets</a></p></li><li><p><a href="https://developer.apple.com/documentation/appintents/appintent/supportedmodes">supportedModes</a></p></li><li><p><a href="https://developer.apple.com/documentation/appintents/creating-your-first-app-intent">Creating your first app intent</a></p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075; Every Wednesday news, every Sunday tips from devs!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 285]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-285</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-285</guid><pubDate>Wed, 23 Sep 2026 21:06:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>Swift 6.4 shipped this week, but the release itself wasn&#8217;t what caught my attention.</p><p>Three completely unrelated posts showed up in my feed within a few days of each other. One was about Swift&#8217;s debug information, another about figuring out where all those extra MB in your app actually went, and another about tracking down mysterious SwiftUI performance problems.</p><p>Different problems. Same frustration:</p><p><strong>Something is wrong, but you can&#8217;t see what&#8217;s happening.</strong></p><p>So this week&#8217;s issue is about debuggability &#8212; and why it deserves more attention than it usually gets.</p><p>I&#8217;d love to hear from you, too: <strong>what&#8217;s the last bug that took way longer than it should have because you couldn&#8217;t see what was actually happening?</strong> Hit reply and tell me.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210653241?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><h1>Debuggability Is a Feature, Not an Afterthought</h1><p><em>with Divya Ravi &#8212; Technical Editor</em></p><p>Three things landed in my feed within about a week of each other, and the authors weren&#8217;t connected.</p><p>Swift 6.4 brought some quieter improvements to how debug information keeps track of where code actually came from. Jacob Bartlett built a browser-based tool that can open up an <code>.ipa</code> and show you where your app size is going. And Natalia Panferova found a useful Xcode launch argument for figuring out why certain <code>ForEach</code> patterns can hurt SwiftUI performance.</p><p>Different problems. Different tools.</p><p>But all three have the same basic idea:</p><p><strong>You can&#8217;t fix what you can&#8217;t see.</strong></p><p>That sounds obvious. In practice, it&#8217;s one of those things that gets pushed aside surprisingly easily.</p><p>Nobody puts &#8220;better debugging information&#8221; on the roadmap when there&#8217;s a feature to ship. Nobody gets excited about spending a sprint figuring out why a binary grew by 40 MB. And &#8220;let&#8217;s make this performance problem easier to diagnose&#8221; rarely beats a customer-facing feature in a planning meeting.</p><p>Usually, someone just gets annoyed enough to investigate it.</p><p>That&#8217;s what happened with these pieces. Someone got tired of guessing, found a way to make the problem visible, and then shared it.</p><p>I think about this a lot when working in an older codebase.</p><p>When you&#8217;re new to a system, the hardest part usually isn&#8217;t understanding the business logic. It&#8217;s figuring out <strong>why the system behaves the way it does</strong>.</p><p>Why is this build so slow?</p><p>Why did the binary suddenly get bigger?</p><p>Why is this view rendering again?</p><p>Which module did this code actually come from?</p><p>Why did changing something three layers away suddenly fix the problem?</p><p>The answers might all be there. The problem is that the system doesn&#8217;t always make them easy to see.</p><p>That&#8217;s why I think good debugging tools are a form of documentation.</p><p>Normal documentation tells you how the system is <em>supposed</em> to work.</p><p>Good debugging information tells you what the system is <em>actually doing</em>.</p><p>And unlike a wiki page, that information can stay pretty close to reality because it&#8217;s coming directly from the system.</p><p>There&#8217;s another reason this feels especially relevant right now.</p><p>We&#8217;re generating more code than ever with AI. Agents can write functions, refactor code, add tests, work across branches, and make surprisingly large changes very quickly.</p><p>But when something goes wrong, we still need to understand <strong>why</strong>.</p><p>Is the generated code slower?</p><p>Did it make the binary larger?</p><p>Did it introduce an unexpected rendering path?</p><p>Did an agent change something three layers down?</p><p>The more code we produce, the more important it becomes to have good ways of looking inside that code.</p><p>And none of the interesting examples this week needed an AI agent or a shiny new framework.</p><p>They started with a much simpler idea:</p><p><strong>If you can&#8217;t see the problem, make it visible.</strong></p><p>So here&#8217;s something I&#8217;m going to keep in mind when planning engineering work.</p><p>If there&#8217;s one thing that has made us guess over and over again &#8212; build performance, a recurring crash, view rendering, binary size, whatever it is &#8212; maybe &#8220;make this easier to understand&#8221; deserves to be an actual engineering task.</p><p>Not something we keep saying we&#8217;ll get to later.</p><p>Because sometimes the fastest way to fix the next problem is to make the current one easier to see.</p><p>And if you have a debugging mystery that has eaten way too much of your time, hit reply and tell me about it. I&#8217;m collecting stories for a future issue.</p><p>I have a feeling we&#8217;re all dealing with at least one.</p><div><hr></div><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><p style="text-align: center;">Four pieces, all published within the same eight-day window, looking at the same problem from different parts of the stack: the compiler, the build artifact, and the view tree.</p><h3><a href="https://www.swift.org/blog/swift-6.4-released/">Swift 6.4 Released</a></h3><p>The big release of the week. Swift Build becomes the default SwiftPM backend, Subprocess reaches 1.0, and Swift&#8217;s interop story gets deeper across C++, Java, and WebAssembly.</p><p>There&#8217;s also a quieter debugging improvement in here that makes the next article particularly interesting.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.swift.org/blog/module-tracking-in-debug-info/">Module Tracking in Swift Debug Info</a></h3><p>Ever had a debugging session where you know the code is there, but you&#8217;re not quite sure which module it actually came from?</p><p>Adrian Prantl explains the new <code>-debug-module-path</code> compiler option in Swift 6.3/6.4 and how it makes that information much easier for the debugger to find.</p><p>A small change, but the kind of thing you appreciate when you&#8217;re deep in a complicated debugging session.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://nilcoalescing.com/blog/DiagnosingForEachPerformanceIssuesInSwiftUILazyContainers/">Diagnosing ForEach performance issues in SwiftUI lazy containers</a></h3><p><code>ForEach</code> looks harmless. Sometimes it isn&#8217;t.</p><p>Natalia Panferova shows how the <code>-LogForEachSlowPath YES</code> launch argument can expose <code>ForEach</code> patterns that quietly hurt performance in lazy containers.</p><p>If you&#8217;ve ever stared at a SwiftUI view wondering why it keeps doing more work than it should, this is worth a look.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://kylebrowning.com/posts/ble-between-two-simulators/">Connecting two iOS simulators over BLE</a></h3><p>Two iOS Simulators talking to each other over Bluetooth LE.</p><p>Yes, really.</p><p>Kyle Browning shows how to connect two simulator instances so you can test things like pairing, proximity, and handoff without needing two physical devices sitting on your desk.</p><p>A clever trick for a problem that usually means reaching for another phone.</p><p><strong><span>&#128205;Level: </span>Advanced</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187; </h1><h3><a href="https://fatbobman.com/en/posts/from-pbxproj-to-xcproj/">From pbxproj to xcproj: Xcode Project Configuration Gets a JSON Format</a></h3><p>The familiar <code>.pbxproj</code> file is getting a new counterpart: <code>project.xcproj</code>, a JSON-based project format.</p><p>The obvious benefit is fewer merge conflicts and easier parsing.</p><p>The more interesting question is what this means for tooling &#8212; especially now that AI agents are increasingly reading and modifying project configuration directly.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://sarunw.com/posts/build-siri-experiences-across-apps-part-1-onscreen-awareness/">Build Siri experiences across apps, Part 1: Onscreen Awareness</a></h3><p>Siri understanding &#8220;this&#8221; or &#8220;that&#8221; sounds simple until you think about what the system actually needs to know.</p><p>Sarun walks through Siri&#8217;s onscreen awareness and how views can be connected to App Entities using <code>NSUserActivity</code> or view annotations.</p><p>This is the first part of a larger series, so there&#8217;s more to come.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://mcmizzle.com/blog/pip-wont-start-avkit-1001/">isPictureInPicturePossible was true and PiP still wouldn&#8217;t start</a></h3><p>Here&#8217;s a familiar kind of iOS bug:</p><p>The API says everything is fine.</p><p>It still doesn&#8217;t work.</p><p>McMizzle walks through the Picture-in-Picture issue from the initial symptom to the actual cause, including the repro and fix.</p><p>These are some of my favorite debugging posts because the final answer is usually something you would never guess from the original symptom.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://swiftwithmajid.com/2026/09/22/backporting-swiftui-apis/">Backporting SwiftUI APIs</a></h3><p>A new SwiftUI API looks great. Your deployment target says, &#8220;Not so fast.&#8221;</p><p>Majid Jabrayilov walks through two clean ways to backport newer SwiftUI APIs: a dedicated <code>Backport</code> namespace or availability-annotated methods that can eventually be removed when your minimum iOS version catches up.</p><p>The second approach has a particularly nice property: you can grep for the old code later and know exactly what can go.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/hello/september26/">Hello Developer: September 2026</a></h3><p>Apple&#8217;s September roundup is here, with the latest Apple Event recap, new App Store Connect analytics videos, the Apple Intelligence Group Lab from WWDC26, Cupertino developer events, and documentation updates across Core Spotlight, Safari, SwiftUI, and Metal.</p><p>A good monthly skim if you want to know what&#8217;s changed without digging through everything yourself.</p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://troz.net/post/2026/fun-with-ai/">Fun with AI</a></h3><p>Sarah Reichelt writes about using Claude during a major web-development redesign &#8212; but the interesting part isn&#8217;t just what AI helped with.</p><p>It&#8217;s where she drew the line.</p><p>She was happy to use AI for parts of the technical work, while keeping her actual writing firmly human.</p><p>That feels increasingly relevant as AI gets better at touching more parts of our work. Not everything needs to be automated just because it can be.</p><p><strong>&#128205;Level: Beginner</strong></p><h3><a href="https://blog.kulman.sk/terminal-native-setup-for-parallel-coding-agents/">My terminal-native setup for parallel coding agents</a></h3><p>Running several coding agents at once sounds great.</p><p>Until you have three terminals open and no idea which agent changed what.</p><p>Igor Kulman shares the tmux + worktree setup he uses to run multiple agents without losing track of them.</p><p>It fits nicely with this week&#8217;s theme too: parallel agents are useful, but only if you can still see what&#8217;s going on.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Design &#127912; </h1><h3><a href="https://developer.apple.com/news/?id=dgmr27r7">How Klemens Strasser prioritized inclusivity with Art of Fauna</a></h3><p>Klemens Strasser&#8217;s Apple Design Award-winning puzzle game <em>Art of Fauna</em> was built around vintage animal illustrations, but there&#8217;s more to the story than the visual design.</p><p>The game was designed with accessibility in mind and tested with Screen Curtain for blind and low-vision players.</p><p>Even better, 20% of the proceeds goes toward wildlife conservation.</p><p>It&#8217;s a nice example of accessibility being part of the product itself rather than something added at the end.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Tutorials&#128210;</h1><h3><a href="https://tanaschita.com/testing-swift-testing-parameterized-tests/">Parameterized tests and test organization with Swift Testing</a></h3><p>If your tests are starting to look like the same test copied five times with different inputs, parameterized tests are probably worth a look.</p><p>Natascha Fadeeva walks through <code>@Test(arguments:)</code>, <code>@Suite</code>, and a cleaner way to organize related Swift Testing cases.</p><p>Simple idea. Much less test duplication.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://danielsaidi.com/blog/2026/09/15/using-automator-workflows-to-compress-images-and-pdfs">Compressing images and PDFs from the Finder context menu</a></h3><p>A tiny automation, but a useful one.</p><p>Daniel Saidi shows how to add an Automator workflow to Finder so you can compress images and PDFs directly from the right-click menu.</p><p>Not everything needs to be a massive automation project. Sometimes the best ones are the little things you use every week.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Devs for Devs: Being a Good Developer Isn’t Enough to Build Your Own Product]]></title><description><![CDATA[From world-class developers, straight to you.]]></description><link>https://thosewhoswift.substack.com/p/devs-for-devs-being-a-good-developer</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/devs-for-devs-being-a-good-developer</guid><pubDate>Sun, 20 Sep 2026 20:30:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello! &#128075;</p><p>In today&#8217;s issue, Aliaksandra Asichka is sharing her knowledge behind indie development.</p><p>She&#8217;s an AI Engineer and iOS Developer who combines artificial intelligence with mobile development to turn ideas into real products. Aliaksandra experiments with new technologies, builds her own projects, and shares practical discoveries from her journey with the developer community.</p><p>Is it enough to be a good dev to ship a product?</p><p>P.S. You can check out Aliaksandra&#8217;s work on <strong><a href="https://www.linkedin.com/in/aliaksandra-asichka-2004b35b/">LinkedIn</a></strong>.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-6TM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!-6TM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!-6TM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!-6TM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-6TM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:329035,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/212755281?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-6TM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!-6TM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!-6TM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!-6TM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb974c293-6b27-4f9b-88f3-f0486ab50b61_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075;</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><span>As developers, we are used to thinking in terms of solutions.</span></p><p><span>There is a problem. We understand the technology. We design the architecture, choose the tools, write the code, fix the bugs, ship the feature.</span></p><p><span>So when a developer decides to build their own product, the natural assumption is that the hardest part will be building it.</span></p><p><span>I thought so too.</span></p><p><span>After working on my own iOS product, MirrorFit, I realised that writing the code was probably the most predictable part of the entire journey.</span></p><p><span>The much harder questions were:</span></p><ul><li><p><strong><span>Who actually needs this?</span></strong></p></li><li><p><strong><span>What do they need it for?</span></strong></p></li><li><p><strong><span>Will they come back?</span></strong></p></li><li><p><strong><span>How do I reach them?</span></strong></p></li><li><p><strong><span>And what should I change when the market doesn&#8217;t behave the way I expected?</span></strong></p></li></ul><p><span>These are questions engineering skills don&#8217;t necessarily prepare you for.</span></p><h2>It Started With Technology &#8212; but Technology Wasn&#8217;t the Product</h2><p><span>MirrorFit started with an idea between me and my co-founder.</span></p><p><span>The original idea was technically interesting: use the camera and face tracking to recognise facial movements and turn them into exercises.</span></p><p><span>I had been practising face fitness myself for a couple of years.</span></p><p><span>So I didn&#8217;t just see a piece of technology that could track a face. I could see how it could actually be used.</span></p><p><span>I started thinking about which exercises could realistically be tracked, how they could be performed in front of a camera, and how computer vision could turn something that was traditionally a set of instructions into an interactive experience.</span></p><p><span>That was the moment when the technology became a product idea.</span></p><p><span>And that experience taught me something I still consider important:</span></p><p><strong><span>Technology is rarely the product by itself.</span></strong></p><p><span>As technical people, we are naturally attracted to interesting technology. A new model, a difficult computer vision problem, an unusual architecture, a new framework &#8212; these things are fun to build.</span></p><p><span>But the fact that something is technically interesting doesn&#8217;t mean that people need it.</span></p><p><span>The first question shouldn&#8217;t always be:</span></p><blockquote><p><em><span>What can I build with this technology?</span></em></p></blockquote><p><span>Sometimes it should be:</span></p><blockquote><p><em><span>Who has a problem that this technology can solve better?</span></em></p></blockquote><h2>The Technical Trap: Making the Product Better Instead of Getting It Into People&#8217;s Hands</h2><p><span>Our next mistake was much more familiar.</span></p><p><span>We spent time improving the product before launch.</span></p><p><span>The design wasn&#8217;t quite right. The content wasn&#8217;t quite ready. There were things we wanted to polish. We wanted the product to feel complete.</span></p><p><span>Meanwhile, the market was moving.</span></p><p><span>Someone released a similar product before we did.</span></p><p><span>And, as if that wasn&#8217;t enough, the name we had chosen was taken by someone else for another fitness-related product.</span></p><p><span>That was an unpleasant lesson in timing.</span></p><p><span>While we were thinking about how to make our product better, someone else was already shipping.</span></p><p><span>Looking back, I would launch the MVP much earlier.</span></p><p><span>Not because the first version would have been better, but because it would have given us something much more valuable than another polished feature: </span><strong><span>real information</span></strong><span>.</span></p><p><span>A product sitting in development gives you assumptions.</span></p><p><span>A product in the hands of users gives you evidence.</span></p><h2><span>The Audience Became More Important Than the Feature Set</span></h2><p><span>Our first testers were friends, people from women&#8217;s communities, and readers from Threads.</span></p><p><span>Eventually, I collected around 100 people in TestFlight.</span></p><p><span>But I wasn&#8217;t just looking for people to click through the app.</span></p><p><span>Even while we were developing it, I was asking questions:</span></p><ul><li><p><span>Do you know what face fitness is?</span></p></li><li><p><span>Which face fitness bloggers do you follow?</span></p></li><li><p><span>Have you taken any courses?</span></p></li><li><p><span>How much time would you realistically spend on a facial workout?</span></p></li><li><p><span>What would make you use an app like this regularly?</span></p></li></ul><p><span>These conversations taught me more than I expected.</span></p><p><span>And surprisingly, the most useful research wasn&#8217;t a sophisticated framework or a spreadsheet.</span></p><p><span>It was talking to people.</span></p><p><span>Personal conversations helped me understand what people actually meant, rather than what I assumed they meant.</span></p><p><span>I also became a very active user of my own product. I tested it after practically every deployment.</span></p><p><span>At some point, I was completely immersed in it.</span></p><p><span>That was useful because I could immediately feel when something was inconvenient or didn&#8217;t make sense.</span></p><p><span>But it also created a danger.</span></p><p><span>When you build something yourself, it is very easy to confuse </span><strong><span>your own understanding of the product</span></strong><span> with </span><strong><span>the market&#8217;s understanding of the product</span></strong><span>.</span></p><p><span>Those are not the same thing.</span></p><h2><span>The Android Request Was More Than an Android Request</span></h2><p><span>One of the recurring pieces of feedback was surprisingly simple:</span></p><blockquote><p><span>&#8220;When will you have Android?&#8221;</span></p></blockquote><p><span>We had built an iOS application, and our audience kept reminding us that the market was bigger than the platform we had chosen.</span></p><p><span>Another recurring request was localisation.</span></p><p><span>These requests were uncomfortable because they were not really about features we could simply add to the backlog.</span></p><p><span>They exposed a bigger question:</span></p><p><strong><span>Who are we actually building this for, and what prevents them from using it?</span></strong></p><p><span>That is an important distinction.</span></p><p><span>User feedback doesn&#8217;t always mean that you should blindly implement whatever users request.</span></p><p><span>But every repeated request is a signal.</span></p><p><span>The job is to understand what is behind it.</span></p><h2><span>The Painful Part: Accepting who the Product Was Actually For</span></h2><p><span>One of the decisions that took the longest to accept was our target audience.</span></p><p><span>Initially, we wanted MirrorFit to work for both women and men.</span></p><p><span>It sounded reasonable.</span></p><p><span>Why make a face fitness product specifically for women?</span></p><p><span>Men can have the same interest in their appearance. Men can have wrinkles. Men can care about their skin and face.</span></p><p><span>And men actually helped us during testing. Around 5% of our testers were men, and their feedback was useful.</span></p><p><span>But testing also showed something else.</span></p><p><span>Trying the product and having a reason to keep using it are two different things.</span></p><p><span>The long-term motivation was much stronger among women.</span></p><p><span>The problem wasn&#8217;t that our competitors had somehow forgotten that men existed.</span></p><p><span>If almost every product in the category was positioning itself primarily toward women, perhaps there was a reason.</span></p><p><span>We had been looking at the market through the lens of what we </span><em><span>wanted</span></em><span> the product to be.</span></p><p><span>Eventually, we had to take off the rose-coloured glasses.</span></p><p><span>We didn&#8217;t fundamentally change the concept.</span></p><p><span>We changed our understanding of who was most likely to need it.</span></p><p><span>And that distinction matters.</span></p><p><strong><span>Sometimes a pivot isn&#8217;t abandoning your original idea. Sometimes it is understanding your original idea more clearly.</span></strong></p><h2><span>Changing the Design Hurt more than Changing the Positioning</span></h2><p><span>There was another part of the process that was surprisingly difficult for me: the design.</span></p><p><span>If you decide that you&#8217;re building a product primarily for women, the product needs to look and feel like something your audience actually wants to use.</span></p><p><span>That sounds obvious.</span></p><p><span>It wasn&#8217;t obvious while we were building it.</span></p><p><span>As a developer, I naturally wanted the functionality to be right first.</span></p><p><span>If the tracking worked, the exercises worked, the flow worked &#8212; that felt like progress.</span></p><p><span>But users don&#8217;t experience your app as a technical achievement.</span></p><p><span>They experience it as a product.</span></p><p><span>They see the visual language, the content, the onboarding, the tone, the exercises, the value proposition.</span></p><p><span>And then they decide whether they want to spend their time with it.</span></p><p><span>Changing the design meant letting go of something we had already spent time creating.</span></p><p><span>That was painful precisely because I liked the product we had.</span></p><p><span>But liking what you&#8217;ve built is not the same as knowing that the market likes it.</span></p><h2><span>Then Came the App Store Reality Check</span></h2><p><span>At some point, I caught myself thinking:</span></p><p><strong><span>Why isn&#8217;t the App Store promoting us?</span></strong></p><p><span>We had built the product. We had testers. We had launched.</span></p><p><span>Where was the growth?</span></p><p><span>The number that eventually stood out to me was 91 downloads.</span></p><p><span>On paper, 91 downloads is not an impressive App Store success story.</span></p><p><span>But I started looking at it differently.</span></p><p><span>Those 91 downloads didn&#8217;t appear because I wrote good Swift code.</span></p><p><span>They happened because I had built something, launched it, talked about it, created content, found people, explained the product, fixed it, shipped it again and kept trying.</span></p><p><span>And I realised:</span></p><p><strong><span>91 downloads were actually my achievement.</span></strong></p><p><span>That sounds small when you compare it with successful apps.</span></p><p><span>But when you build your first product, you quickly discover that every number has a story behind it.</span></p><p><span>One download means someone noticed you.</span></p><p><span>One tester means someone gave you their time.</span></p><p><span>One piece of feedback means someone cared enough to tell you what they thought.</span></p><p><span>And when the numbers are small, you don&#8217;t have the luxury of hiding behind dashboards.</span></p><p><span>You have to understand where every user came from.</span></p><h2><span>Once It&#8217;s Your Product, You&#8217;re Not Just the Developer</span></h2><p><span>This was probably the biggest mindset change for me.</span></p><p><span>When you work as a developer, you can specialise.</span></p><p><span>You can be responsible for iOS while someone else handles product.</span></p><p><span>Someone else researches users.</span></p><p><span>Someone else writes copy.</span></p><p><span>Someone else runs marketing.</span></p><p><span>Someone else looks at acquisition.</span></p><p><span>Someone else worries about positioning.</span></p><p><span>When it&#8217;s your own product, there is no &#8220;someone else&#8221;.</span></p><p><span>You are the developer.</span></p><p><span>But you&#8217;re also the product manager.</span></p><p><span>You&#8217;re the marketer.</span></p><p><span>You&#8217;re doing user research.</span></p><p><span>You&#8217;re looking at analytics.</span></p><p><span>You&#8217;re thinking about design.</span></p><p><span>You&#8217;re answering users.</span></p><p><span>You&#8217;re deciding what to build next.</span></p><p><span>And sometimes you&#8217;re staring at the App Store wondering why nobody is downloading the thing you spent months building.</span></p><h2><span>What I Would Do Differently</span></h2><p><span>If I started MirrorFit again, I would do three things differently.</span></p><h3><span>1. Launch the MVP Earlier</span></h3><p><span>Not when everything feels finished.</span></p><p><span>Earlier.</span></p><p><span>The market is a much better source of information than another month of internal polishing.</span></p><h3><span>2. Start Marketing Earlier</span></h3><p><span>I used to think marketing was something that came after the product.</span></p><p><span>Now I see it as part of product development.</span></p><p><span>You need to know whether you can reach the people you&#8217;re building for.</span></p><p><span>You need to understand what language makes them stop scrolling.</span></p><p><span>You need to know what they think the product is before you can know whether your positioning works.</span></p><h3><span>3. Get Comfortable Changing Your Mind</span></h3><p><span>This is probably the hardest one.</span></p><p><span>When you&#8217;ve built something yourself, you&#8217;re emotionally attached to it.</span></p><p><span>You&#8217;ve spent evenings on it.</span></p><p><span>You&#8217;ve solved technical problems.</span></p><p><span>You&#8217;ve made decisions.</span></p><p><span>You&#8217;ve imagined what it could become.</span></p><p><span>Then someone tells you they want something different.</span></p><p><span>Your first reaction is often:</span></p><p><strong><span>But that&#8217;s not what we wanted to build.</span></strong></p><p><span>Sometimes that&#8217;s exactly when you need to listen.</span></p><h2><span>Should You Build Alone?</span></h2><p><span>Building your own product also taught me something that has nothing to do with technology.</span></p><p><span>It is hard.</span></p><p><span>Really hard.</span></p><p><span>Not because there is always too much work, but because there are periods when nothing seems to happen.</span></p><p><span>You can have a period where everything moves forward and you feel that the product is finally taking off.</span></p><p><span>Then you can have a period where the numbers don&#8217;t move, marketing doesn&#8217;t work, and you start questioning whether any of it makes sense.</span></p><p><span>I don&#8217;t think building alone is impossible.</span></p><p><span>But I don&#8217;t think it&#8217;s for everyone either.</span></p><p><span>Having a co-founder or even a friend who genuinely supports you can make a huge difference.</span></p><p><span>The useful thing about having another person is not only dividing the workload.</span></p><p><span>Sometimes you simply need someone who is having a good day when you&#8217;re having a bad one.</span></p><p><span>If you&#8217;re building with someone, your periods of doubt may not happen at exactly the same time.</span></p><p><span>And that can be enough to keep moving.</span></p><h2><span>The Lesson I Actually Took from Building a Product</span></h2><p><span>I started this project because I wanted to experience both sides of the process: being a developer and being an entrepreneur.</span></p><p><span>I wanted to release my first application, understand what happens beyond writing code, and see what it actually takes to put a product on the App Store.</span></p><p><span>I got that experience.</span></p><p><span>But I also learned that there isn&#8217;t a perfect formula for what to do first.</span></p><p><span>You can&#8217;t only listen to yourself.</span></p><p><span>You can&#8217;t only listen to the market.</span></p><p><span>If you only listen to yourself, you can spend months building something technically impressive that nobody needs.</span></p><p><span>If you only listen to the market, you can end up changing your product every time somebody asks for a new feature.</span></p><p><span>The real skill is finding the balance.</span></p><p><strong><span>Listen to yourself enough to have a vision.<br>Listen to the market enough to challenge your assumptions.</span></strong></p><p><span>And if you&#8217;re thinking about building your own product, I would start with something much less exciting than opening your IDE.</span></p><p><span>Study your competitors.</span></p><p><span>If you can&#8217;t find any, don&#8217;t automatically assume you&#8217;ve discovered an untouched opportunity. Ask yourself whether you&#8217;re solving a problem people actually have.</span></p><p><span>If competitors do exist, don&#8217;t be discouraged by them. Ask yourself how you can create more value.</span></p><p><span>Find a co-founder, a friend, or someone who can support you through the inevitable ups and downs.</span></p><p><span>And most importantly:</span></p><p><strong><span>Don&#8217;t be afraid to change the idea.</span></strong></p><p><span>A pivot doesn&#8217;t necessarily mean that the original idea was bad.</span></p><p><span>Sometimes it means you&#8217;ve finally learned enough about the market to build the version that has a chance to matter.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075; Every Wednesday news, every Sunday tips from devs!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 284]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-284</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-284</guid><pubDate>Thu, 17 Sep 2026 06:08:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>Apple announced a foldable iPhone this week and gave developers about six weeks before it ships. This issue looks at what that means for iOS developers: what we can safely work on now, what&#8217;s still uncertain, and where it may make sense to wait.</p><p>Are you starting the refactor now, or waiting for Xcode 27.1?</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210653241?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h1>Shipping for Hardware You Can't Fully Test Yet</h1><p><em>with Divya Ravi &#8212; Technical Editor</em></p><p>Apple announced the foldable iPhone on September 9 and gave developers about six weeks before it reaches customers. The interesting part isn&#8217;t just learning the new APIs. It&#8217;s figuring out what&#8217;s safe to change while the platform is still evolving.</p><p>It&#8217;s easy to treat this as an API-learning exercise: learn the new size classes, reserved regions, ArrangementViews, and start updating layouts. But the harder part is that we still don&#8217;t know everything. Some APIs are still coming through the Xcode 27.1 beta, and developers haven&#8217;t had real hardware to validate how apps behave across different fold positions.</p><p>So the real question is: <strong>how much should you build now?</strong></p><p>I&#8217;ve made both mistakes. Earlier in my career, I rebuilt a navigation layer around a beta API that changed significantly before GM. That meant weeks of rework. I&#8217;ve also waited too long on a new platform capability and ended up playing catch-up after launch.</p><p>For iPhone Duo, I&#8217;d split the work into two buckets.</p><p><strong>The safe stuff:</strong> move away from orientation checks, use size classes properly, and look for hardcoded screen dimensions or aspect-ratio assumptions. None of this is really foldable-specific. It&#8217;s good iOS engineering that the new form factor makes more important.</p><p><strong>The uncertain stuff:</strong> anything that depends on the exact behavior of the hinge, fold positions, or APIs that are still changing. That&#8217;s where I&#8217;d wait. Building against a stable API is a one-time cost. Building against something that changes every beta can mean repeated rework.</p><p>There&#8217;s a broader lesson here too. Platforms are never really finished; they just usually feel finished because we don&#8217;t see the seams. A new form factor makes those seams obvious.</p><p>For me, the approach is simple: make the changes that are clearly correct today, and keep the hardware-specific pieces flexible until we know more.</p><p><strong>What are you doing&#8212;starting now, or waiting for Xcode 27.1?</strong></p><div><hr></div><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><p style="text-align: center;">Apple&#8217;s iPhone Duo developer resources landed September 9, and there&#8217;s already a lot of discussion around what developers should actually do with them.</p><p>Here are four takes I found useful, each looking at a different part of the problem.</p><h3><a href="https://blakecrosley.com/blog/iphone-duo-for-developers">iPhone Duo for Developers: The 1.42 Problem and the SDK Gap</a></h3><p>A detailed look at the display specs and the current SDK situation. It&#8217;s particularly useful if you want to understand what&#8217;s available today versus what&#8217;s still coming in Xcode 27.1.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://www.swiftjectivec.com/iphone-duo-first-developer-good-to-knows/">iPhone Duo: First Developer Good-to-Knows</a></h3><p>Jordan&#8217;s practical summary of Apple&#8217;s six Tech Talks. It covers the things most developers will care about first: adaptive layouts, safe areas, camera behavior, and multitasking.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://dev.to/arshtechpro/iphone-duo-for-ios-developers-what-actually-changes-in-your-swift-code-5gc5">iPhone Duo for iOS Developers: What Actually Changes in Your Swift Code</a></h3><p>A more code-focused walkthrough covering size classes, reserved regions, and ArrangementViews. Good if you want to get straight to the API changes.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://samwize.com/2026/09/14/how-much-should-developers-invest-in-iphone-duo/">How Much Should Developers Invest in iPhone Duo?</a></h3><p>A useful counterpoint to the &#8220;start preparing immediately&#8221; approach. It looks at expected adoption and the engineering cost of supporting another form factor.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://dev.to/javaskr/what-apples-september-2026-event-means-for-developers-3pnk">What Apple's September 2026 Event Means for Developers</a></h3><p>A broader look at Apple&#8217;s September announcements, including the A20 Pro, on-device AI, AirPods 5, and the new Watch hardware.</p><p><strong><span>&#128205;Level: </span>Beginner</strong></p><h3><a href="https://shopify.engineering/back-to-native">Native is now the future of mobile at Shopify</a></h3><p>Shopify explains its move from React Native back to native Swift and Kotlin. The interesting part is the economics: AI coding tools have changed how the team thinks about maintaining native codebases.</p><p>Not directly related to iPhone Duo, but definitely worth reading if you&#8217;re thinking about where AI-assisted development is taking mobile engineering.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187;</h1><h3><a href="https://www.danielemargutti.com/en/2026/09/14/csv-parser-twenty-seconds/">When Intuition Lies: Lessons from a CSV Parser</a></h3><p>A good reminder that performance problems aren&#8217;t always where we expect them. The example starts with a CSV parser that looks reasonable but spends most of its time on string allocation rather than file I/O.</p><p>Also interesting: the release build was dramatically faster than debug.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://ivanthinking.net/2026/09/15/action-runner-ios-architecture/">Action Runner: An iOS Architecture for Isolating Business Logic</a></h3><p>A different approach to keeping business logic away from controllers. The <code>Action</code>/<code>RunnableAction</code> pattern and shared <code>RunContext</code> are worth looking at if your architecture is starting to develop circular dependencies.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://antongubarenko.substack.com/p/ios-27-crashreportextension-framework">iOS 27: CrashReportExtension Framework</a></h3><p>A look at Apple&#8217;s new crash-reporting extension in iOS 27. Instead of doing everything from a signal handler, the crash can be inspected from a separate system-managed process.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://albertodebortoli.com/2026/09/08/inover-the-air-localizations-in-a-modular-ios-app/">In-House Over-the-Air Localizations in a Modular iOS App</a></h3><p>An interesting production example from Just Eat Takeaway: updating localized strings remotely without requiring a full App Store release.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/news/?id=vn8abkxx">Get ready for iPhone Duo</a></h3><p>Apple&#8217;s main developer hub for iPhone Duo. It brings together the six Tech Talks, Group Labs, Q&amp;A sessions, and the Xcode 27.1 beta.</p><h3><a href="https://developer.apple.com/news/?id=k1mtkt1k">App Store submissions now open for the latest OS releases</a></h3><p>Xcode 27 RC is available, and App Store Connect is accepting submissions built against the latest OS releases. The new minimum-SDK requirements take effect across Apple platforms in April 2027.</p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://www.pointfree.co/blog/posts/228-lazystate-1-0-now-available-to-everyone">LazyState 1.0: Now Available to Everyone</a></h3><p>Point-Free&#8217;s LazyState macro is now at 1.0. It creates SwiftUI state lazily and keeps it tied to the view&#8217;s identity.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://vision.engineer/posts/object-tracking-updates-in-visionOS-27-and-iOS-27/">Object Tracking Updates in visionOS 27 and iOS 27</a></h3><p>A look at the new object-tracking capabilities in visionOS 27 and iOS 27, including an accessibility project that combines object tracking with Vision Pro and iPhone.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Design&#127912;</h1><h3><a href="https://www.sagarunagar.com/blog/iphone-duo-human-interface-guidelines/">Designing for iPhone Duo: The HIG Guide in 5 Minutes</a></h3><p>A quick walkthrough of Apple&#8217;s HIG guidance for the new form factor, including adaptive layouts, safe areas, fold displacement, and the new arrangement containers.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Tutorials&#128210;</h1><h3><a href="https://www.kodeco.com/53560697-moving-from-xctest-to-swift-testing">Moving from XCTest to Swift Testing</a></h3><p>A practical guide to moving from XCTest to Swift Testing, including the new <code>@Suite</code> and <code>@Test</code> APIs and updated assertion syntax.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><h1 style="text-align: center;">Video &#127909;</h1><h3><a href="https://developer.apple.com/videos/play/tech-talks/111461/">Prepare your app for iPhone Duo</a></h3><p>If you only watch one of Apple&#8217;s six iPhone Duo videos, I&#8217;d start here. It covers flexible layouts, size classes, asymmetric safe areas, and the reserved-regions API..</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Devs for Devs: A Working App Is Only the Beginning]]></title><description><![CDATA[From world-class developers, straight to you.]]></description><link>https://thosewhoswift.substack.com/p/devs-for-devs-a-working-app-is-only</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/devs-for-devs-a-working-app-is-only</guid><pubDate>Sun, 13 Sep 2026 20:31:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Happy Sunday! &#128075;</p><p>Today, Ekaterina Buyanova is sharing her secrets with us.</p><p>She&#8217;s an iOS developer with a strong interest in SwiftUI, creative coding, and building polished user experiences. Ekaterina also shares her experiments and practical discoveries with the iOS community through technical posts and open-source projects.</p><p>Where would the indie app development process go?</p><p>P.S. You can check out Ekaterina&#8217;s work on <strong><a href="https://www.linkedin.com/in/ekaterina-buyanova-9a448b331/">LinkedIn</a></strong>, and <strong><a href="https://github.com/katrin-buyanova">Git</a></strong>.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U4nm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!U4nm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!U4nm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!U4nm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!U4nm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:303305,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/212052173?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!U4nm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!U4nm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!U4nm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!U4nm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff714ce4f-1731-4cdd-b73b-9ff544022801_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075;</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><span>I&#8217;ve come across many technically impressive apps. Complex features are implemented exceptionally well, the problems they solve are genuinely meaningful, and some solutions clearly reflect serious engineering work. The developer understands the system inside out and can bring almost any idea to life.</span></p><p><span>To the person who built it, the interface feels obvious - they know its logic, understand the purpose of every element, and remember exactly where each action is. A new user arrives without any of that context. They open the app for the first time and see only what&#8217;s right in front of them.</span></p><p><span>At that point, the technical complexity behind the product takes a back seat. If users have to search for the primary action, figure out the navigation, or guess what they&#8217;re supposed to do next, there&#8217;s a good chance they&#8217;ll simply close the app.</span></p><p><span>Not because the product is bad or the feature isn&#8217;t useful, but because </span><strong><span>the cost of that first interaction was higher than the value they had a chance to see.</span></strong></p><p><span>While working on my own product, I started looking at development more and more from this perspective. </span><strong><span>A working feature doesn&#8217;t automatically create a good user experience, and a technically finished app isn&#8217;t necessarily a finished product.</span></strong></p><h3><span>The First Few Seconds Matter</span></h3><p><span>A user forms their first impression of an app before they have a chance to appreciate its functionality. First, they need to understand something much simpler: where they are, what they can do, and where to start.</span></p><p><span>And this is where developers face a problem - </span><strong><span>we know our own interfaces too well</span></strong><span>. After dozens of hours working on an app, the meaning of every icon and the location of every button feel obvious. But that knowledge exists in the developer&#8217;s head, not the user&#8217;s.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EJV0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EJV0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!EJV0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!EJV0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!EJV0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EJV0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!EJV0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!EJV0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!EJV0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!EJV0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F997c54bb-50be-4f47-be62-3e6561e688d0_1536x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>That&#8217;s why a working interface isn&#8217;t necessarily an understandable one. A button can trigger the right action, data can save correctly, and navigation can work without errors - while the user is still looking at the screen wondering, </span><em><span>&#8220;What am I supposed to do next?&#8221;</span></em></p><p><span>One thing worth paying close attention to is </span><strong><span>visual hierarchy</span></strong><span>. If five elements on a screen all look equally important, we&#8217;re essentially asking the user to decide what matters most. The primary action should be easy to recognize, while secondary options should appear when they become relevant.</span></p><p><span>The same applies to familiar iOS patterns. It can be tempting to invent a more original interaction, but navigation bars, sheets, tab bars, and system controls already have one major advantage: </span><strong><span>users know how they work before they even open our app</span></strong><span>. If a standard pattern solves the problem well, there should be a good reason to replace it with something unfamiliar.</span></p><p><span>One of the most revealing tests requires almost no tools at all. Give the app to someone who has never seen it before, ask them to complete a specific task, and don&#8217;t explain anything.</span></p><p><span>The moment you feel the urge to say, </span><em><span>&#8220;No, tap here first,&#8221;</span></em><span> stop. You&#8217;ve probably just found a part of the interface that was obvious to the developer - but not to the user.</span></p><p><span>That&#8217;s why testing shouldn&#8217;t focus only on whether a user can complete an action, but also on whether they can figure out how to do it on their own.</span></p><h2 style="text-align: justify;"><span>Beyond the Happy Path</span></h2><p><span>During development, it&#8217;s easy to focus on the ideal scenario: the user opens a screen, performs an action, and gets the expected result. But real users don&#8217;t follow our test scenarios.</span></p><p><span>They leave fields empty, interrupt actions halfway through, lose their connection, or return to the app after an update with existing data. Sometimes they simply use a feature in a way </span><strong><span>we never expected</span></strong><span>. This is where the difference between </span><strong><span>a working feature and a resilient product</span></strong><span> becomes much more visible.</span></p><p><span>The happy path is only the tip of the iceberg.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZrsS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZrsS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!ZrsS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!ZrsS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!ZrsS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZrsS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZrsS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!ZrsS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!ZrsS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!ZrsS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd55fd4f3-2a4a-4534-a05f-de2f9cde97c5_1536x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>Below the surface are all the states that are easy to overlook when everything goes according to plan: empty states, denied permissions, invalid input, interrupted flows, errors and recovery, migrations, and unexpected combinations of existing data. So when testing a feature, it can be useful to reverse the usual question.</span></p><p><span>Instead of asking only:</span></p><p><strong><span>&#8220;Does the main flow work?&#8221; </span></strong><span>ask: </span><strong><span>&#8220;What happens when the user goes beyond it?&#8221;</span></strong></p><p><span>The happy path shows that a feature works.</span></p><p><strong><span>What happens around it shows how well the product was actually designed.</span></strong></p><h2 style="text-align: justify;"><span>More Than Working Code</span></h2><p><span>When building your own app, it&#8217;s easy to focus on the question we, as developers, naturally ask: </span><strong><span>&#8220;Does it work?&#8221;</span></strong></p><p><span>It&#8217;s an important question - but for an indie developer, it can&#8217;t be the last one.</span></p><p><span>There may be no separate designer, QA engineer, or product team to challenge our decisions from another perspective. We have to deliberately step outside the implementation and look at the experience we&#8217;ve created.</span></p><p><span>The code can work perfectly. But users don&#8217;t experience the implementation - </span><strong><span>they experience the product.</span></strong></p><p><strong><span>And that&#8217;s why a working app is only the beginning.</span></strong></p><h2 style="text-align: justify;"><span>WaterMeApp</span><strong><span> &#127807;</span></strong></h2><p><span>I recently released </span><strong><a href="https://apps.apple.com/pl/app/watermeapp/id6779227958"><span>WaterMeApp</span></a></strong><span>, an iOS app designed to make everyday plant care simple and easy to keep track of.</span></p><p><span>I wanted the experience to feel calm and intuitive, with a thoughtfully designed interface and a soft, nature-inspired color palette. One of the features I especially wanted to include was </span><strong><span>shared plant care</span></strong><span> - you can share your plants with someone while you&#8217;re away and stay updated when they&#8217;re watered.</span></p><p><span>Building WaterMeApp gave me the opportunity to put many of the ideas from this article into practice - and reinforced the idea that making an app work is only one part of building a product.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Nwgi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Nwgi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png 424w, https://substackcdn.com/image/fetch/$s_!Nwgi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png 848w, https://substackcdn.com/image/fetch/$s_!Nwgi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png 1272w, https://substackcdn.com/image/fetch/$s_!Nwgi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Nwgi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png" width="1456" height="563" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:563,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Nwgi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png 424w, https://substackcdn.com/image/fetch/$s_!Nwgi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png 848w, https://substackcdn.com/image/fetch/$s_!Nwgi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png 1272w, https://substackcdn.com/image/fetch/$s_!Nwgi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2432a5-f02e-4199-af23-6fbb6dbfdd19_2017x780.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075; Every Wednesday news, every Sunday tips from devs!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 283]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-283</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-283</guid><pubDate>Wed, 09 Sep 2026 20:05:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>Four pieces this week, and every one of them starts the same way: someone stops assuming and goes and checks.</p><p>What they find is the same shape of problem wearing different clothes &#8212; the screen isn&#8217;t the device, the toolbar doesn&#8217;t have infinite room, the view model doesn&#8217;t need every layer of the template, and the data isn&#8217;t shaped the way the schema assumed it was.</p><p>Hit reply and tell us: what&#8217;s an assumption baked into your codebase that you&#8217;ve never actually gone and verified?</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210653241?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h1>The Space You Have Isn't the Space You Assumed with</h1><p><em>with Divya Ravi &#8212; Technical Editor, iOS engineer</em></p><p>Every iOS developer has, at some point, written <code>UIScreen.main.bounds</code> and moved on with their day. It compiles. It runs. It&#8217;s been &#8220;fine&#8221; for years. It is also, this week&#8217;s reading reminded me, quietly wrong in an increasing number of situations &#8212; and the fix for that particular wrongness turns out to be a pretty good description of a habit worth having everywhere else in the codebase too.</p><p>Livsy&#8217;s piece on <code>UIWindowScene.effectiveGeometry</code> is the clearest version of the argument. <code>UIScreen.main</code> tells you about the physical display. It has never told you about the space your app actually occupies &#8212; and on modern iOS, with Stage Manager, external displays, split-screen multitasking, and resizable windows, &#8220;the space your app actually occupies&#8221; and &#8220;the physical display&#8221; can be wildly different numbers. The fix isn&#8217;t complicated once you know it exists: ask the window scene what space you&#8217;ve actually been given, respond to it when it changes, and stop treating a <code>Screen</code> API as a stand-in for a question it was never built to answer. The API name is almost a tell. It says <code>Screen</code>. It was never going to tell you about your window.</p><p>Natascha Fadeeva&#8217;s toolbar piece is the same idea at a smaller scale. A toolbar with five items looks perfect at design time, on your test device, in your test locale. Then someone rotates the device, or picks a language where your labels run long, or uses a Dynamic Type setting you didn&#8217;t test at, and &#8220;five items&#8221; stops fitting. The interesting part of her piece isn&#8217;t the overflow API itself &#8212; it&#8217;s the framing. Toolbar space was never a fixed quantity you could design against once. It&#8217;s a runtime value, same as any other piece of state, and it deserves the same respect you&#8217;d give any other thing that can change out from under you.</p><p>Antoine van der Lee&#8217;s MVVM piece pushes on a version of this that&#8217;s easier to miss because it isn&#8217;t visual. A lot of SwiftUI architecture advice assumes a ceiling of complexity that most individual screens never actually reach &#8212; every screen gets a view model, every view model gets injected dependencies, every dependency gets a protocol, whether the screen in question is a five-state settings toggle or a real, gnarly, stateful flow. The piece isn&#8217;t an argument against MVVM. It&#8217;s an argument for measuring the actual complexity of the actual screen before reaching for the full pattern &#8212; the same move as checking <code>effectiveGeometry</code> instead of assuming the device size, just applied to code shape instead of screen shape.</p><p>And then there&#8217;s Fatbobman&#8217;s piece on SwiftData modeling, which is the same lesson at the layer furthest from the screen. He makes the case that optimization problems in SwiftData are, more often than people expect, modeling problems wearing a performance costume &#8212; a slow fetch is frequently the result of a schema shaped around how the data was imagined rather than how it&#8217;s actually queried and related. You can spend a long time tuning predicates and indexes against a model that was wrong from the start. Model first, from what&#8217;s actually there. Optimize second.</p><p>Four different layers &#8212; the screen, the toolbar, the view model, the schema &#8212; and the same underlying move at each one: replace an assumption you inherited or guessed at with a value you actually checked. None of these are dramatic bugs. They&#8217;re the unglamorous kind of wrong that just sits there working &#8220;well enough&#8221; until a configuration shows up that your assumption didn&#8217;t cover, and by then it&#8217;s not a code review comment, it&#8217;s a support ticket.</p><p>I don&#8217;t think the lesson is &#8220;distrust everything.&#8221; Most of your assumptions are fine, most of the time, or nobody would ship anything. But it&#8217;s worth keeping a running list, somewhere in the back of your head, of the assumptions in your own codebase you&#8217;ve never actually gone and checked &#8212; the ones you inherited from a tutorial, a template, or your own past self on a smaller, simpler version of this project. This week&#8217;s four pieces are what it looks like when someone finally checks one.</p><p>Hit reply and tell me: what&#8217;s the assumption in your codebase you&#8217;d bet money is wrong, if you ever got around to checking it?</p><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><h3><a href="https://livsycode.com/uikit/effective-geometry-layout-without-uiscreen-main/">Effective Geometry: Layout Without UIScreen.main</a></h3><p>A practical swap-in for <code>UIScreen.main.bounds</code>, including the delegate hook for reacting when your window&#8217;s actual geometry changes mid-session.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://tanaschita.com/swiftui-toolbar-visibility-overflow/">Managing Toolbar Visibility and Overflow in SwiftUI</a></h3><p>The API walkthrough for items that don't fit &#8212; display modes, overflow menus, and which item order to pick to control what collapses first.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.avanderlee.com/swiftui/mvvm-architectural-coding-pattern-to-structure-views/">MVVM in SwiftUI: Using View Models Without Overengineering</a></h3><p>A concrete walkthrough of when a view model actually earns its place in a SwiftUI screen &#8212; and the lighter-weight alternatives for the ones that don&#8217;t need one.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://fatbobman.com/en/posts/swiftdata-optimization-starts-with-modeling/">SwiftData: Optimization Starts with Modeling</a></h3><p>Hands-on guidance on structuring SwiftData relationships and attributes correctly from the start, before reaching for a fetch-descriptor tweak or a new index.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://www.swift.org/blog/whats-new-in-swift-august-2026/">What's New in Swift: August 2026 Edition</a></h3><p>Swift.org's monthly roundup of language, tooling, and ecosystem changes from across the Swift project &#8212; the fastest way to catch anything that shipped last month without combing through individual proposal threads.</p><p><strong><span>&#128205;Level: </span>Intermediate</strong></p><h3><a href="https://sarunw.com/posts/readable-codable-errors/">Codable Errors Are Finally Readable in Swift 6.3</a></h3><p>Sarun Wongpatcharapakorn covers Swift 6.3's overhaul of <code>DecodingError</code>'s printed output &#8212; turning a wall of nested enum cases into something you can actually read at a glance the next time a JSON payload doesn't match your model.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187;</h1><h3><a href="https://toyboy2.medium.com/dont-move-your-xcdatamodeld-until-you-read-this-33687945f0c5">Don't Modularize Your .xcdatamodeld Until You Read This</a></h3><p>Lee young-jun documents a rough real-world attempt to split a Core Data model across feature modules &#8212; the crashes and codegen mismatches that showed up, and the manual model classes plus an <code>xcrun momc</code> build step that ended up being the actual fix. &#9888;&#65039; Published May 12, 2026 &#8212; outside our usual 2-3 week window; included as a flagged exception since Core Data modularization pain doesn't really go stale. See verification notes.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/news/?id=w5ngl9k2">Upcoming Changes to Rosetta Support for Intel-Based macOS Apps</a></h3><p>Apple laid out the endgame for Rosetta on Apple silicon: user-facing warnings starting in macOS 26.4, and macOS 27 as the last release that runs Intel-only apps at all. If you're still shipping an Intel-only build, the clock is now explicit.</p><h3><a href="https://developer.apple.com/news/?id=artln8br">Tax and Price Updates for Apps, In-App Purchases, and Subscriptions</a></h3><p>New VAT and digital sales tax rules in Morocco, the Republic of the Congo, and Tanzania are triggering automatic App Store price and proceeds updates starting September 14 &#8212; worth a look if you sell in any of those storefronts.</p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://swiftwithmajid.com/2026/09/01/building-ai-features-using-foundation-models-multimodal-input/">Building AI Features Using Foundation Models: Multimodal Input</a></h3><p>Majid Jabrayilov walks through feeding images alongside text into Apple's on-device Foundation Models framework &#8212; a concrete look at multimodal prompting without leaving the device or paying a cloud-API bill per request.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://blog.kulman.sk/pi-is-now-my-ai-harness/">Why Pi Is My AI Coding Harness</a></h3><p>Igor Kulman explains why he standardized on Pi, a minimal coding agent, as a stable workflow layer &#8212; building his own extensions and reusable skills once so he can swap the underlying AI model without rebuilding his whole setup each time.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://blog.jacobstechtavern.com/p/advanced-agentic-engineering">Advanced Agentic Engineering</a></h3><p>Jacob Bartlett shares practical techniques for running multiple AI coding agents at once &#8212; verification loops, automation, and guardrails that keep several agents productive in parallel instead of turning into several times the review burden.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://gioscalzo.com/blog/ai-accelerated-mobile-development-nslondon/">AI-Accelerated Mobile Development: Lessons from My NSLondon Talk</a></h3><p>Giordano Scalzo revisits his NSLondon talk on building iOS/macOS apps with AI coding agents &#8212; including the moment a missing sandbox entitlement broke his QR-code app's file saving, and what that taught him about the engineer's job shifting from writing code to directing it.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Design&#127912;</h1><h3><a href="https://nilcoalescing.com/blog/BuildingASunburstDiagramInSwiftCharts/">Building a Sunburst Diagram in Swift Charts</a></h3><p>Matthaus Woolard builds a hierarchical sunburst chart using Swift Charts' newer arc and sector marks &#8212; a solid reference the next time you need to visualize nested categories and Swift Charts' built-in chart types don't quite fit the shape of your data.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">Community&#129309;</h1><h3><a href="https://www.objc.io/blog/2026/09/04/the-end-of-swift-talk/">The End of Swift Talk</a></h3><p>After a decade and more than 500 episodes, objc.io is ending Swift Talk, its long-running weekly Swift video series &#8212; auto-renewals are already off, and the full archive is set to go public by year's end. The end of one of the format's earliest and most consistent teaching resources.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Devs for Devs: Domain, Data, Presentation - What Clean Architecture Actually Buys You on iOS]]></title><description><![CDATA[From world-class developers, straight to you.]]></description><link>https://thosewhoswift.substack.com/p/devs-for-devs-domain-data-presentation</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/devs-for-devs-domain-data-presentation</guid><pubDate>Sun, 06 Sep 2026 19:31:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It&#8217;s already the 5th issue, wow, time flies! Thank you for reading. &#129395;</p><p>Today, Vadim Krutov is sharing his architectural planning vision.</p><p>He&#8217;s an iOS developer who has been building apps since 2017, with a strong interest in Swift, SwiftUI, architecture, and modern Apple development.</p><p>As the creator of <a href="https://swiftloop.dev/">Swiftloop</a>, he shares practical lessons drawn from real-world iOS development, from debugging tricky issues to exploring APIs and architectural decisions.</p><p>Vadim also contributes to the Swift ecosystem through open-source packages and technical writing.</p><p>Let&#8217;s check out what CLEAN architecture will bring us.&#128071;</p><p>P.S. You can check out Vadim&#8217;s work on <strong><a href="https://www.linkedin.com/in/vadim-krutov/">LinkedIn</a></strong>, and <strong><a href="https://swiftloop.dev">website</a></strong>.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b4K-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!b4K-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!b4K-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!b4K-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b4K-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:260779,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/211525839?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!b4K-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!b4K-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!b4K-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!b4K-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F342d6c7b-fe53-4931-9ead-0f4b8d6fbc4d_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075;</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><span>Most iOS codebases don&#8217;t start layered. They start as a SwiftUI view or a view model that fetches, decodes, maps, validates, and renders, all in one file, because that&#8217;s the fastest way to ship the first screen. It works right up until the second screen needs the same business rule, or the backend changes a field, or someone tries to unit test a network call without booting a simulator. At that point the tax comes due: business logic is welded to URLSession, UI state is welded to Core Data, and touching any one of them risks breaking all three.</span></p><p><span>Clean Architecture&#8217;s answer is to split the app into three layers &#8212; Domain, Data, Presentation &#8212; with one rule holding them together: dependencies point inward, toward Domain. Data and Presentation both know about Domain; Domain knows about neither. That single rule is where most of the payoff comes from, and most of the ways teams get it wrong come from quietly breaking it. Here&#8217;s what that looks like in practice, fifteen lessons in.</span></p><h2><span>Why Bother Splitting into Three Layers at All</span></h2><p><span>The immediate win is navigability. When every piece of code has exactly one of three possible homes &#8212; business rules in Domain, persistence and networking in Data, screens and state in Presentation &#8212; a developer who has never seen the codebase can still find their way around it. &#8220;Where&#8217;s the discount logic?&#8221; has one answer, not &#8220;check the view model, or maybe the API client, or possibly that extension file.&#8221;</span></p><p><span>The bigger win shows up when you make a large decision later. Decide to move from MVVM to TCA, or from Combine to async/await in your view layer? That only touches Presentation. Decide to replace Core Data with SwiftData, or REST with GraphQL? That only touches Data. Domain doesn&#8217;t move either time, because Domain never depended on </span><em><span>how</span></em><span> data was fetched or </span><em><span>how</span></em><span> the screen was built &#8212; only on the contracts. Layering doesn&#8217;t prevent big rewrites; it just makes sure a rewrite in one layer doesn&#8217;t force a rewrite in the other two.</span></p><h2>Start With the Domain Layer, and Keep It Pure</h2><p><span>If you&#8217;re building all three layers from scratch, start with Domain. Both Data and Presentation end up depending on it, so the contracts it defines &#8212; protocols, models, use cases &#8212; are the thing everything else is built against. Defining them early isn&#8217;t mandatory, but it saves you from designing a repository interface and a view model in parallel, guessing at what the other side needs.</span></p><p><span>The part that&#8217;s easy to get wrong isn&#8217;t the ordering, it&#8217;s the purity. Domain should stay clear of UIKit and SwiftUI &#8212; nothing in your business rules needs to know a screen or a view exists. Foundation basics like Date, UUID, and Data are a different case; they&#8217;re everyday vocabulary, not framework leakage, and most domain models end up depending on at least one of them. Combine is more of a team call than a hard line &#8212; if your Data layer is already built on it, Domain may end up depending on it too, since there&#8217;s often no realistic way around that. Beyond keeping UI frameworks out, how strictly you draw the line is ultimately a team agreement. Some teams knowingly let a specific framework into Domain because it clearly pays for itself in their context. That&#8217;s a fine trade-off to make deliberately; it&#8217;s just worth making it on purpose rather than by accident, since every exception is one more thing standing between Domain and being buildable and testable on its own.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:&quot;06c6bdc5-0172-438f-806f-95b4b0c6c395&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">struct Order: Identifiable, Equatable {

    let id: UUID

    let items: [OrderItem]

    let status: OrderStatus

}

protocol OrderRepository {

    func fetchOrders() async throws -&gt; [Order]

    func place(_ order: Order) async throws

}</code></pre></div><p><span>Nothing here says how orders are fetched or stored. That&#8217;s what makes the &#8220;develop independently&#8221; promise real: you can write and test every business rule against this protocol before a single Data or Presentation type exists, with no simulator and no mocked network layer, just a fake conforming to OrderRepository.</span></p><h2>The Repository Is a Facade, Not a Pipe</h2><p><span>Domain depends on repository contracts, never on individual data source contracts. Everything that can go wrong on the data side &#8212; a flaky network, a cache that needs merging with a fresh response, a backend field that got renamed &#8212; should be invisible past the repository boundary. That only works if you design the contract carefully up front, because loosening it later usually means touching every call site.</span></p><p><span>Two things tend to hide inside that facade: mapping and error translation. A DTO and a domain model often look identical, and that similarity makes people want to collapse them into one type. Don&#8217;t &#8212; the DTO is what absorbs an API change; the domain model is what the rest of the app is allowed to depend on.</span></p><p><span>Error translation has the same shape, and the same rule about which layer owns what: OrderError has to be defined in Domain, not Data, because Domain can never depend on Data &#8212; if the error type lived next to RemoteOrderRepository, nothing above it in Domain would be allowed to catch it.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;rust&quot;,&quot;nodeId&quot;:&quot;13b163e4-2086-4d19-ac01-9ed98c5adc26&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-rust">// Domain

enum OrderError: Error {

    case unavailable(underlying: Error)

    case corruptedResponse

    case unknown(underlying: Error)

}

// Data

struct OrderDTO: Decodable {

    let id: String

    let items: [OrderItemDTO]

    let status_code: Int

}

struct RemoteOrderRepository: OrderRepository {

    private let client: APIClient

    private let cache: OrderCache

    func fetchOrders() async throws -&gt; [Order] {

        do {

            let dtos = try await client.get(&#8221;/orders&#8221;, as: [OrderDTO].self)

            let orders = dtos.map(Order.init)

            await cache.save(orders)

            return orders

        } catch let error as URLError {

            if let cached = await cache.readAll() { return cached }

            throw OrderError.unavailable(underlying: error)

        } catch is DecodingError {

            throw OrderError.corruptedResponse

        } catch {

            throw OrderError.unknown(underlying: error)

        }

    }

}</code></pre></div><p><span>That trailing </span><code>catch { throw OrderError.unknown(underlying: error) }</code><span> isn&#8217;t decoration &#8212; Swift requires a do-catch to be exhaustive, so without an unconditional catch at the end, this doesn&#8217;t compile. It also happens to be the right place to route anything you didn&#8217;t anticipate into the same domain vocabulary, rather than letting a raw URLError or some third one slip through uncaught.</span></p><p><span>Nothing in Order ever needs to know that status_code used to be a String and became an Int in some backend release, and nothing in a view model needs to catch a URLError &#8212; it catches </span><code>OrderError.unavailable</code><span> and shows a retry button. That&#8217;s the whole point of the facade: whatever happened on the way in, Domain only ever sees its own vocabulary, defined on its own side of the boundary.</span></p><h2>Use Cases Have to Earn Their Keep</h2><p><span>A use case that only forwards to a single repository method is dead weight with extra steps:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;rust&quot;,&quot;nodeId&quot;:&quot;be3e681f-e81e-4e92-af20-3b4e2b7e206f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-rust">struct FetchOrdersUseCase {

    let repository: OrderRepository

    func execute() async throws -&gt; [Order] {
        try await repository.fetchOrders()
    }

}</code></pre></div><p><span>This shows up in a lot of codebases that adopted Clean Architecture by the checklist rather than by need, and it adds a maintenance cost &#8212; one more type, one more place to inject, one more file to open &#8212; without adding any behavior. Strictly speaking, this is what canonical Clean Architecture calls for: Presentation and Data both depend on Domain, and a use case is the shape that dependency is supposed to take, even when all it does is forward to one repository method. Following that to the letter is correct by the pattern; it&#8217;s also exactly where the maintenance burden shows up fastest. Plenty of teams bend the rule slightly and inject repositories directly into view models or reducers instead, skipping the pass-through use case entirely. That&#8217;s a small deviation from the canonical shape, not a different architecture &#8212; and whether to make that trade is a team decision, not something the pattern settles for you.</span></p><p><span>A use case earns its place when it&#8217;s actually doing something: coordinating more than one repository, enforcing a business rule, or sharing logic across several view models or reducers that would otherwise duplicate it.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;d99b606b-63eb-4942-b55c-2009441d3b0b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">struct PlaceOrderUseCase {

    let orders: OrderRepository

    let inventory: InventoryRepository

    func execute(_ order: Order) async throws {

        try await inventory.reserve(order.items)

        do {
            try await orders.place(order)
        } catch {
            try? await inventory.release(order.items)

            throw error
        }
    }
}</code></pre></div><p><span>That&#8217;s worth a dedicated type. Depending on two repository protocols here is fine, and doesn&#8217;t bend the dependency rule &#8212; </span><code>OrderRepository</code><span> and </span><code>InventoryRepository</code><span> are both Domain contracts, so the use case is coordinating within Domain the entire time; it never reaches into Data or Presentation to do it. Whether your team draws the &#8220;worth a use case&#8221; line here is genuinely a team decision, not an architectural law &#8212; just be honest about which side of it a given use case is on.</span></p><h2>Let Swift&#8217;s Own Capabilities Do Some of the Work</h2><p><span>Keeping track of what the language itself gives you matters as much as the layering does. If you&#8217;re designing a new repository contract today, reach for async/await before Combine &#8212; it maps more directly onto what a repository call actually is, a single request with a single result, and it keeps Data-layer code out of publisher composition that Presentation didn&#8217;t ask for.</span></p><p><span>That same instinct &#8212; use the language, don&#8217;t route around it &#8212; is where a lot of Clean Architecture guidance for iOS shows its Android or Java roots most clearly, usually one level below the repository. A </span><code>MyLocalDataSource</code><span>: </span><code>MyLocalDataSourceProtocol</code><span> pair for something as small as a Keychain-backed token is often more type than the job needs on this platform:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;b424e203-2521-4d37-a5f5-c8b67a7e67e3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">@propertyWrapper
struct KeychainStored&lt;Value: Codable&gt; {

    let key: String

    var wrappedValue: Value? {
        get { KeychainHelper.read(key, as: Value.self) }
        set { KeychainHelper.write(key, newValue) }
    }

}

struct AuthRepository {
    @KeychainStored(key: &#8220;auth.token&#8221;) private var token: AuthToken?
}</code></pre></div><p><span>Same behavior as a dedicated data source type, a fraction of the code to maintain. Not every data source deserves a full type &#8212; some deserve one line.</span></p><h2>Make the Boundaries Real: Modules and Composition Roots</h2><p><span>Folder structure and good intentions don&#8217;t stop someone from importing a Presentation type into Domain under deadline pressure &#8212; and once that happens once, &#8220;Domain is pure&#8221; becomes a claim nobody can actually verify by reading the code. Splitting Domain, Data, and Presentation into separate Swift Package targets turns that mistake into a compiler error instead of a code review miss, with faster incremental builds as a side effect:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;f4429f08-c8e9-4510-ba9b-59b4206c7415&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">// Package.swift

targets: [

    .target(name: &#8220;Domain&#8221;),

    .target(name: &#8220;Data&#8221;, dependencies: [&#8221;Domain&#8221;]),

    .target(name: &#8220;Presentation&#8221;, dependencies: [&#8221;Domain&#8221;]),

    .target(name: &#8220;App&#8221;, dependencies: [&#8221;Data&#8221;, &#8220;Presentation&#8221;])

]</code></pre></div><p><span>Data and Presentation can both import Domain, but neither can import each other, and Domain can&#8217;t import either. That&#8217;s the dependency rule, enforced by the build system rather than by convention.</span></p><p><span>Somewhere, all of this still has to get wired together &#8212; a concrete RemoteOrderRepository handed to a PlaceOrderUseCase handed to a view model. Keep that assembly in one obvious place, whether that&#8217;s your app&#8217;s entry point, a set of factories, or a small DI container, rather than letting each screen construct its own dependency graph. A composition root is the one place in the app that&#8217;s allowed to know about all three layers at once; that&#8217;s a feature, not a violation of the rule.</span></p><h2><span>Why this matters even more when an agent is writing the code</span></h2><p><span>Clear layering used to be mostly a human-navigation aid. It&#8217;s now also a determinism aid. An agent generating a new feature against a codebase with well-defined layers produces far more predictable output than one working against a flat pile of SwiftUI views, because &#8220;where does this code go&#8221; has an answer before the agent has to invent one. And the same clarity that helps the agent generate code helps you review it afterward &#8212; you know a repository implementation belongs in Data, so when an agent&#8217;s diff puts networking code in a view model, that&#8217;s immediately visible as wrong, not something you have to reconstruct from context. Architecture that used to be a courtesy to the next developer is now also a courtesy to the next agent, and to whoever reviews what it wrote.</span></p><h2><span>Gotchas</span></h2><p><strong><span>Don&#8217;t reach for three layers and three modules on a three-screen prototype.</span></strong><span> Clean Architecture&#8217;s cost is real and mostly fixed &#8212; it doesn&#8217;t scale down. Applied to something that will be thrown away in a sprint, it&#8217;s not rigor, it&#8217;s overhead with nothing to show for it.</span></p><p><strong><span>A repository contract designed around one data source rarely survives a second one untouched.</span></strong><span> If </span><code>OrderRepository</code><span> was shaped entirely by what the REST client returns, adding a local cache or a second backend tends to expose assumptions baked into the contract, not just the implementation. The careful-design-up-front advice isn&#8217;t boilerplate &#8212; it&#8217;s where most contract rewrites originate.</span></p><p><strong><span>Module boundaries pay for themselves at team and codebase size, not before.</span></strong><span> Three SPM targets on a solo two-week project is mostly extra </span><code>Package.swift</code><span> ceremony. The build-time enforcement earns its keep once more than one person is touching the code and a misplaced import would otherwise sit unnoticed until it&#8217;s expensive to unwind.</span></p><h2><span>Summary</span></h2><p><span>Everything above is really one rule wearing different clothes: dependencies point inward, and Domain doesn&#8217;t know Data or Presentation exist. Start there, keep it pure, make repositories the only seam between Domain and the outside world, and let use cases prove they&#8217;re doing real work before you write another one. The layering doesn&#8217;t make big decisions disappear &#8212; switching frameworks, backends, or architectures inside a layer is still work &#8212; it just makes sure that work stays inside the layer it started in, whether the one making the change is you, a teammate, or an agent.</span></p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075; Every Wednesday news, every Sunday tips from devs!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 282]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-282</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-282</guid><pubDate>Wed, 02 Sep 2026 21:05:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>&#8220;<em>It just works</em>&#8221; is doing a lot of quiet work in that sentence.</p><p>This week&#8217;s reads are about the rules hiding underneath four things that feel automatic: how SwiftUI decides a view needs to redraw, which property wrapper actually owns your object, what git really deletes when you force-push over two weeks of work, and what happens when you stop clicking through Xcode and start scripting it directly.</p><p>Hit reply and tell us: what&#8217;s the last &#8220;it just works&#8221; feature that turned out to have rules you had to learn the hard way?</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210653241?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h1>Every &#8220;It Just Works&#8221; Has Rules You Haven&#8217;t Learned Yet.</h1><p><em>with Divya Ravi &#8212; Technical Editor, iOS engineer</em></p><p>Every framework sells you a story. SwiftUI redraws the right views. Git deletes what you tell it to delete. Xcode is where you click things until the app runs. The story is convenient right up until the moment it&#8217;s wrong, and then you find out the story was never the whole mechanism &#8212; just the part someone decided you didn&#8217;t need to see.</p><p>Natalia Panferova&#8217;s piece on SwiftUI data dependencies is a good place to start, because &#8220;SwiftUI re-renders when state changes&#8221; is the kind of sentence that&#8217;s true enough to stop you from asking a better question. Which state? Tracked how? Through what boundary? Panferova walks through the actual dependency graph SwiftUI builds, and once you&#8217;ve seen it, the mysterious extra redraw &#8212; or worse, the redraw that never happens &#8212; stops being mysterious. It was never magic. It was a rule you hadn&#8217;t read yet.</p><p>Antoine van der Lee&#8217;s @StateObject-versus-@ObservedObject piece sits right next to that one, because ownership is exactly the kind of thing SwiftUI lets you get away with ignoring until you can&#8217;t. Both wrappers will compile. Both will often behave identically in a demo. The difference only shows up when the view holding an @ObservedObject gets recreated and your &#8220;persistent&#8221; state quietly resets &#8212; at which point you learn, expensively, that one of these wrappers was never supposed to be the source of truth.</p><p>Then there&#8217;s Daniel Saidi&#8217;s story, which is the most viscerally relatable thing I&#8217;ve read this month: an errant <code>git reset</code> and force-push erased two weeks of work on a real project. I felt that in my chest reading it. But the resolution is the actual lesson &#8212; git doesn&#8217;t delete commits when you delete a ref, it just stops pointing at them. The work was never gone. It was unreachable, which is a very different thing from destroyed, and knowing that difference is the kind of fact that turns a five-alarm panic into a five-minute recovery.</p><p>Artem Novichkov&#8217;s MCP-driven Xcode piece rounds this out from a different angle. Instead of learning a hidden rule the hard way, he&#8217;s exploiting one on purpose: Xcode and the simulator were never really &#8220;a GUI,&#8221; they&#8217;re a set of programmable interfaces that a GUI happens to sit in front of. Once you talk to that layer directly &#8212; prompt to build to simulator, no clicking &#8212; a lot of what felt like &#8220;the way Xcode works&#8221; turns out to have been &#8220;the way the GUI chose to expose Xcode.&#8221; Different thing entirely.</p><p>None of these are really stories about bugs. They&#8217;re stories about the gap between the interface you were handed and the system actually running underneath it &#8212; and about what happens once someone bothers to close that gap for you. The dependency graph, the ownership model, the ref/object split, the automatable toolchain: all of it was always there, documented or discoverable, just not part of the story anyone tells you on day one.</p><p>I don&#8217;t think the answer is to distrust every abstraction you use. Most of them earn the trust, most of the time. But it&#8217;s worth noticing which ones you&#8217;ve never actually verified &#8212; which &#8220;it just works&#8221; you&#8217;ve been repeating because it&#8217;s convenient, not because you checked. The rules are usually learnable in an afternoon. The bill for not knowing them tends to come due at a much worse time.</p><p><strong>Hit reply and tell me: what&#8217;s an &#8220;it just works&#8221; in your stack that you&#8217;ve never actually gone and verified?</strong></p><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><h3><a href="https://www.avanderlee.com/swiftui/stateobject-observedobject-differences/">@StateObject Vs. @ObservedObject: The Differences Explained</a></h3><p>Antoine van der Lee breaks down the ownership rules behind SwiftUI's two reference-type property wrappers &#8212; which one actually creates the object, which one just observes it, and why picking the wrong one causes state to quietly reset when a view gets recreated.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://nilcoalescing.com/blog/SwiftUIDataDependenciesAndTheirEffectOnViewUpdates/">SwiftUI Data Dependencies And Their Effect On View Updates</a></h3><p>Natalia Panferova goes past "SwiftUI re-renders when state changes" to the actual dependency graph that decides whether a change gets noticed at all &#8212; essential reading for anyone who's ever debugged a view that wouldn't update, or updated when it had no reason to.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://danielsaidi.com/blog/2026/08/27/some-things-are-never-truly-lost">Some Things Are Never Truly Lost</a></h3><p>After an errant <code>git reset</code> and force-push wiped two weeks of work from a real project, Daniel Saidi discovered that deleting a ref isn't deleting the commits behind it &#8212; and walks through recovering the entire branch with a single command.</p><p><strong>&#128205;Level: Beginner</strong></p><h3><a href="https://artemnovichkov.com/blog/headless-xcode-from-prompt-to-simulator-with-mcp">Headless Xcode: From Prompt To Simulator With MCP</a></h3><p>Artem Novichkov drives Xcode and the iOS Simulator entirely through an MCP server and prompts &#8212; no clicking required &#8212; treating the toolchain as a scriptable interface instead of a GUI you have to operate by hand.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://sarunw.com/posts/trailing-comma-in-swift/">Trailing Commas Beyond Arrays In Swift 6.1</a></h3><p>Sarun Wongpatcharapakorn covers how Swift 6.1 extends trailing-comma support past arrays to parameter lists, tuples, and generics &#8212; a small change that quietly makes every future code review and diff cleaner.</p><p><strong><span>&#128205;Level: </span>Beginner</strong></p><h3><a href="https://www.pointfree.co/blog/posts/223-beta-preview-lazystate">Beta Preview: LazyState</a></h3><p>Point-Free previews LazyState, a library that picks up where WWDC's new <code>@State</code> macro left off, letting SwiftUI view state be initialized lazily <em>and</em> dynamically from data the parent view passes in.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187;</h1><h3><a href="https://www.swiftbysundell.com/articles/warning-for-nested-weak-self-closure-captures/">Why Swift Is Introducing A Warning For Weak Captures Within Nested Closures</a></h3><p>John Sundell explains a new Swift compiler warning that catches a specific, easy-to-miss closure-capture mistake &#8212; and why "just add [weak self]" turns out not to be automatically enough once closures start nesting.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://azamsharp.com/2026/08/29/building-a-reusable-http-client-in-swift.html">Building A Reusable HTTP Client In Swift With async/await</a></h3><p>Mohammad Azam builds a typed, testable networking layer from scratch on async/await &#8212; a solid reference the next time you're tempted to reach for a third-party networking library instead.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.basbroek.nl/multiple-custom-contents-swiftui">Dynamically Setting Accessibility Custom Content Pairs In SwiftUI</a></h3><p>Bas Broek shows how to attach and dynamically update multiple accessibility custom content entries on a view, so VoiceOver users get more than one flattened description of what's on screen.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/news/?id=s6spdug0">Surprise And Shine</a></h3><p>Apple announced a September 9, 2026 event, widely expected to headline the iPhone 18 Pro lineup. Nothing developer-facing has shipped yet, but it's the date to have on your radar.</p><h3><a href="https://developer.apple.com/news/?id=1ptvdtcm">Update: New Domain For Sign In With Apple</a></h3><p>Apple reversed a planned domain change for Sign in with Apple and iCloud+ Hide My Email after developer and user pushback &#8212; the current domain stays in place.</p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://www.natashatherobot.com/p/flow-ai-coding">Getting Into Flow With AI Coding</a></h3><p>Natasha makes the case that AI-assisted coding can deepen flow state rather than break it &#8212; if you stay actively involved: breaking work into small commits, and doing the planning and reviewing yourself instead of handing that over too.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://roland.leth.ro/blog/tech/claude-code-rules-hooks-and-skills-and-how-i-set-mine-up">Claude Code Rules, Hooks And Skills, And How I Set Mine Up</a></h3><p>Roland Leth breaks down Claude Code's three configuration layers &#8212; rules, skills, and hooks &#8212; and how they differ: preferences you state versus procedures you define versus safeguards that get enforced whether the AI "remembers" them or not.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Design&#127912;</h1><h3><a href="https://nilcoalescing.com/blog/BuildingAnAccessibleCalendarInSwiftCharts/">Building An Accessible Calendar Chart With Swift Charts</a></h3><p>Matthaus Woolard builds a calendar-style chart with Swift Charts that also works with VoiceOver &#8212; treating accessibility as part of the chart's design from the start rather than a pass added at the end.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Tutorials &#128210;</h1><h3><a href="https://kylebrowning.com/posts/uploading-to-testflight-from-the-command-line/">Uploading To TestFlight From The Command Line</a></h3><p>Kyle Browning walks through scripting a TestFlight upload without opening Xcode &#8212; a useful building block for anyone trying to get release day off a human's clipboard and onto CI.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://sagarunagar.com/blog/fix-sluggish-xcode-swiftui-previews-mock-data">Fix Sluggish SwiftUI Previews Instantly With Static Mock Data</a></h3><p>Sagar Unagar shows a quick technique for keeping Xcode previews fast: swap live or expensive data sources for static mocks so iterating on a view doesn't mean waiting on a network call every time.</p><p><strong>&#128205;Level: Beginner</strong></p><div><hr></div><h1 style="text-align: center;">Community&#129309;</h1><h3><a href="https://forums.swift.org/t/accepted-se-0541-flexible-swift-c-interoperability-for-packages/89183">SE-0541: Flexible Swift/C Interoperability For Packages</a></h3><p>The Swift Evolution community accepted SE-0541, giving Swift packages more flexible ways to interoperate with C code &#8212; a small but real ecosystem win for anyone bridging C libraries into Swift.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Devs for Devs: From AI-Generated Code to AI-Assisted Engineering]]></title><description><![CDATA[From world-class developers, straight to you.]]></description><link>https://thosewhoswift.substack.com/p/devs-for-devs-from-ai-generated-code</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/devs-for-devs-from-ai-generated-code</guid><pubDate>Mon, 31 Aug 2026 18:00:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let&#8217;s call it some technical issues. &#128517;</p><p>Instead of Sunday, we&#8217;re publishing &#8220;<em>Devs for Devs</em>&#8221; on Monday.</p><p>This week, I&#8217;m happy to have Vinicius Serpa Pansan, an iOS developer with 4 years of experience building apps with a focus on performance and user experience. Based in S&#227;o Paulo, Brazil, he works across the Apple ecosystem with technologies including Swift, SwiftUI, UIKit, and AppKit.</p><p>He shares his experience with AI-assisted app creation. Let&#8217;s check how to check, verify and adapt the AI-assisted code.</p><p>P.S. You can check out Vinicius&#8217;s work on <strong><a href="https://www.linkedin.com/in/viniciusspansan/">LinkedIn</a></strong>, and <strong><a href="https://github.com/OFFSerpa">Git</a></strong>.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7rGe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!7rGe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!7rGe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!7rGe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7rGe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/796b6293-ee21-404b-9e77-77504f644ded_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:295621,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/211137226?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7rGe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!7rGe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!7rGe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!7rGe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796b6293-ee21-404b-9e77-77504f644ded_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075;</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><span>A few months ago I started building Fio, a voice-first journaling app for iOS. The core idea is simple: speaking is faster than typing, so instead of asking someone to type out their day, you just talk, and the app transcribes and saves it. No decision required at the moment of capture, no folder to choose, no structure to commit to. Just speak, and it&#8217;s there.</span></p><p><span>I built it entirely in SwiftUI, and from day one, Claude Code and Codex were part of the actual development process, not a tool I reached for occasionally, but something I used in nearly every session.</span></p><p><span>My first instinct, like most people&#8217;s, was to use AI the obvious way: describe what I wanted, get a working implementation, accept it, move to the next screen. It&#8217;s the fastest path from idea to shipped feature, and for a while, it felt like exactly the right call. Screens came together quickly. Features that would have taken me a full evening to hand-write were done in twenty minutes.</span></p><p><span>Then, a few weeks in, I noticed something that bothered me more than it should have. I&#8217;d finish a session having built something that worked, and if you&#8217;d asked me to explain why it was built that particular way, instead of some other reasonable way, I sometimes couldn&#8217;t answer with confidence. I knew what the code did. I didn&#8217;t always know why it was the right shape for the problem.</span></p><p><span>That gap is easy to ignore when everything is working. It stops being easy to ignore the moment you have to defend a decision, in a code review, in an interview, or just to your future self six months later trying to remember why you did something a certain way.</span></p><p><span>This is the story of how I closed that gap, and the four-step loop I ended up building around it.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0pE-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0pE-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png 424w, https://substackcdn.com/image/fetch/$s_!0pE-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png 848w, https://substackcdn.com/image/fetch/$s_!0pE-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png 1272w, https://substackcdn.com/image/fetch/$s_!0pE-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0pE-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png" width="1456" height="682" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:682,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0pE-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png 424w, https://substackcdn.com/image/fetch/$s_!0pE-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png 848w, https://substackcdn.com/image/fetch/$s_!0pE-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png 1272w, https://substackcdn.com/image/fetch/$s_!0pE-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F45dcd607-0a77-4d62-9879-db3a88e23e84_1600x750.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><span>The Problem with Just Accepting the Output</span></h2><p><span>The default AI workflow most developers fall into is straightforward: describe a problem, get a suggestion, skim it, it looks reasonable, accept it, move to the next task. It&#8217;s fast, and it feels productive, because things keep getting built. Velocity is visible. Understanding isn&#8217;t, so it&#8217;s easy to quietly deprioritize.</span></p><p><span>The moment this stopped being abstract for me was a decision that actually mattered for Fio: how to handle voice transcription. The app&#8217;s entire premise rests on a privacy promise, you speak, it&#8217;s transcribed, and nothing leaves the device unless you explicitly choose to back it up to your own iCloud. No audio ever touches a server. That&#8217;s not a minor implementation detail, it&#8217;s the reason the app exists in the shape it does.</span></p><p><span>If I had approached that the way I approached the first dozen smaller features, describe &#8220;add voice transcription,&#8221; accept the first working version, I likely would have ended up with something that technically transcribed audio just fine, but quietly assumed a server round-trip somewhere in the pipeline, because that&#8217;s the default shape most transcription examples and tutorials reach for online. It would have worked. It would have also violated the one promise the entire app is built around, and I might not have caught it until much later, possibly after the architecture around it was already hard to unwind.</span></p><p><span>Nothing about that outcome would have been the AI&#8217;s fault, to be clear. I was the one who hadn&#8217;t specified the constraint, hadn&#8217;t asked why a particular approach was chosen over another, hadn&#8217;t treated the output as something to interrogate rather than something to accept. The tool did exactly what I asked. I just hadn&#8217;t asked precisely enough, because I wasn&#8217;t in the habit of asking at all.</span></p><p><span>That&#8217;s the moment I stopped treating AI output as a finished answer and started treating it as a first draft that owed me an explanation</span></p><p><span>Here&#8217;s the difference in practice. The version I almost shipped would have looked something like this:</span></p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!galQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png 424w, https://substackcdn.com/image/fetch/$s_!galQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png 848w, https://substackcdn.com/image/fetch/$s_!galQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png 1272w, https://substackcdn.com/image/fetch/$s_!galQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!galQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png" width="1426" height="308" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:308,&quot;width&quot;:1426,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!galQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png 424w, https://substackcdn.com/image/fetch/$s_!galQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png 848w, https://substackcdn.com/image/fetch/$s_!galQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png 1272w, https://substackcdn.com/image/fetch/$s_!galQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbedfb8d8-1ca5-4324-baab-b375509cf78f_1426x308.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><p><span>The version that actually matches Fio&#8217;s privacy promise sets one property explicitly:</span></p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z15R!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png 424w, https://substackcdn.com/image/fetch/$s_!z15R!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png 848w, https://substackcdn.com/image/fetch/$s_!z15R!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png 1272w, https://substackcdn.com/image/fetch/$s_!z15R!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z15R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png" width="1428" height="264" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:264,&quot;width&quot;:1428,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z15R!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png 424w, https://substackcdn.com/image/fetch/$s_!z15R!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png 848w, https://substackcdn.com/image/fetch/$s_!z15R!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png 1272w, https://substackcdn.com/image/fetch/$s_!z15R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46ed2e7d-8448-43d7-abf1-eb5ba1b4f437_1428x264.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><p><span>One line. But it&#8217;s the line that turns &#8220;probably private&#8221; into &#8220;actually private,&#8221; and it&#8217;s exactly the kind of detail that&#8217;s easy to skip when you&#8217;re evaluating from a neutral position instead of comparing against something you wrote yourself first.</span></p><h2><span>The Loop I Built Instead</span></h2><p><span>I didn&#8217;t design this all at once. It came together step by step, each addition responding to a specific way the &#8220;just accept it&#8221; habit had already failed me.</span></p><h3><span>Step 1: Write Your Own Approach Before Asking for One</span></h3><p><span>Before I ask Claude Code or Codex to implement anything non-trivial, I write, in plain language, how I think it should work. Not code. A paragraph, sometimes just a few sentences. What matters isn&#8217;t the polish, it&#8217;s that I commit to an opinion before I see someone else&#8217;s, even an AI&#8217;s.</span></p><p><span>This sounds like a small thing, but it changes the psychology of the whole exchange. When you ask first and see the answer second, you&#8217;re evaluating from a neutral position, and neutral positions default to &#8220;looks fine&#8221; far more often than they should. When you write your own approach first, you have something to compare against, and comparison is a completely different cognitive act than approval. You&#8217;re no longer asking &#8220;does this look right?&#8221; You&#8217;re asking &#8220;where does this differ from what I expected, and why?&#8221;</span></p><p><span>More than once, my written approach turned out to be missing something the AI&#8217;s version accounted for, and I learned from the gap. Just as often, my approach was actually right, and the AI&#8217;s first suggestion took a shortcut I didn&#8217;t want, which I only caught because I had something concrete to compare it to.</span></p><h3><span>Step 2: Let It Implement or Push Back, Not Replace</span></h3><p><span>Once there&#8217;s a direction, the AI&#8217;s role is to build within it, and to flag problems it can see that I can&#8217;t, not to substitute its own direction for mine by default. This is a subtle distinction, but it changes how I write prompts. Instead of &#8220;build a sync mechanism for offline notes,&#8221; I&#8217;ll say something closer to &#8220;here&#8217;s how I want to structure the sync mechanism, tell me what breaks.&#8221;</span></p><p><span>That framing invites pushback instead of discouraging it. And when the pushback comes, it&#8217;s usually specific and useful, because it&#8217;s responding to a concrete proposal rather than generating one from scratch.</span></p><h3><span>Step 3: Ask &#8220;Why This, Not the Alternative&#8221; on Every Non-Trivial Call</span></h3><p><span>This is the step that actually catches the cases like the transcription example, the ones that are quietly wrong by default, not obviously wrong. If I can&#8217;t explain, in my own words, why a piece of implementation exists the way it does, I don&#8217;t move forward, regardless of whether the code compiles and the tests pass.</span></p><p><span>Sometimes the answer the AI gives is genuinely good, something I hadn&#8217;t considered, and I learn from it directly. Sometimes asking the question reveals that I framed the original request incorrectly, and the more useful fix is upstream of the code entirely. Either way, I come out of the exchange knowing something I didn&#8217;t know when I asked.</span></p><h3><span>Step 4: Close Every Session by Summarizing What You Learned</span></h3><p><span>Not what got built. What got understood? At the end of a session, I ask for a short summary of the concepts we touched, framed as though I had to explain them to someone else afterward, a teammate, an interviewer, my future self debugging this in six months.</span></p><p><span>This is the habit that turns a series of coding sessions into something that compounds instead of something that just accumulates. It&#8217;s also, practically speaking, what makes a project like this defensible later. When you can explain not just what a piece of code does but why it exists in that shape, you&#8217;re not reciting an AI&#8217;s explanation from memory, you&#8217;re demonstrating that the understanding is actually yours.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2Sc8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2Sc8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png 424w, https://substackcdn.com/image/fetch/$s_!2Sc8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png 848w, https://substackcdn.com/image/fetch/$s_!2Sc8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png 1272w, https://substackcdn.com/image/fetch/$s_!2Sc8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2Sc8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png" width="1456" height="582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:582,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2Sc8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png 424w, https://substackcdn.com/image/fetch/$s_!2Sc8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png 848w, https://substackcdn.com/image/fetch/$s_!2Sc8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png 1272w, https://substackcdn.com/image/fetch/$s_!2Sc8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b52a7c-556e-4fb3-942b-c9f37233b123_1600x640.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><span>What Changed</span></h2><p><span>Fio still isn&#8217;t public, so I&#8217;m not going to pretend this loop is the reason the app is good. What I can say with confidence is narrower and, I think, more useful: I know exactly why every non-trivial decision in it was made. That wasn&#8217;t true a few weeks earlier, when I was moving faster and understanding less, and it&#8217;s a different thing entirely from knowing that the code works.</span></p><p><span>That distinction matters more than it might seem to at first. Code that works is a snapshot. Understanding why it works, and why the alternative wasn&#8217;t chosen, is what lets you extend it, debug it under pressure, or explain it convincingly to someone else, none of which &#8220;it works&#8221; guarantees on its own.</span></p><p><span>I think this is the trade worth naming clearly, because it&#8217;s easy to miss in the current wave of AI-assisted development: speed was never actually the constraint holding most developers back. Understanding was, and it&#8217;s always been the harder, slower thing to build, which is exactly why it was the first thing to get cut whenever a faster path appeared. AI is, among other things, a very fast path. It removed a real excuse people used to have for skipping the &#8220;why&#8221;, there simply wasn&#8217;t time, someone had to just ship the thing. That excuse is mostly gone now. What&#8217;s left is a choice, made consciously or not, every time you accept a suggestion: build the loop that forces understanding, or don&#8217;t, and get code that works until, one day, it doesn&#8217;t, and you&#8217;re left debugging something you never actually understood in the first place.</span></p><p><span>The question worth asking isn&#8217;t &#8220;am I using AI?&#8221; anymore. At this point, most of us are, and the answer says less than it used to. The more useful question is narrower: after this session, do I know more than I did before it started, or am I just further along?</span></p><p><span>If you&#8217;re building something with AI as an active part of your process and want to compare notes on what&#8217;s working and what isn&#8217;t, feel free to reach out. I&#8217;m always happy to talk through it.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zqyB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zqyB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png 424w, https://substackcdn.com/image/fetch/$s_!zqyB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png 848w, https://substackcdn.com/image/fetch/$s_!zqyB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png 1272w, https://substackcdn.com/image/fetch/$s_!zqyB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zqyB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png" width="1456" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zqyB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png 424w, https://substackcdn.com/image/fetch/$s_!zqyB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png 848w, https://substackcdn.com/image/fetch/$s_!zqyB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png 1272w, https://substackcdn.com/image/fetch/$s_!zqyB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef424f14-44ee-41ba-bc91-26bdaa8148a2_1600x800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe if you&#8217;re new here! &#128075; Every Wednesday news, every Sunday tips from devs!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 281]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-281</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-281</guid><pubDate>Wed, 26 Aug 2026 20:38:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>Ship day gets the screenshots, the release notes, and the celebratory post. Day two gets the bug reports.</p><p>This week&#8217;s reads are about that much longer stretch of software life: a 14-year-old app replacing its database, an open-source maintainer asking others to take ownership, a developer measuring what iOS background scheduling actually does in the wild, and an engineering lead making decisions before certainty arrives.</p><p>Hit reply and tell us: what&#8217;s the oldest piece of software you&#8217;re still responsible for keeping alive?</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210653241?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h1>Shipping Is the Easy Part. Sustaining It Is the Job.</h1><p><em>with Divya Ravi &#8212; Technical Editor, iOS engineer</em></p><p>Ship day gets all the ceremony. The build goes green, App Review says yes, and for a few hours the project feels finished.</p><p>Then somebody finds the edge case.</p><p>Maybe it is an old device you no longer own. Maybe it is data created six versions ago. Maybe it is not even your bug, but it is happening inside your app, so the distinction is not especially useful to the person staring at a broken screen.</p><p>That is the part of shipping we rarely celebrate: the years after the release, when the job becomes less about adding something new and more about keeping a promise you already made.</p><p>Daniel Saidi&#8217;s Wally is a good example. The app is 14 years old, and version 7 replaces Realm with SwiftData. Saidi describes a much cleaner architecture, less storage code, better integration with modern Apple frameworks, and private iCloud sync. It is tempting to summarize that as a straightforward modernization. It probably did not feel straightforward while changing the foundation of an app that has been accumulating users and data since 2012.</p><p>There is a point in every long-lived codebase when leaving the old system alone stops being the conservative choice. The workaround has a workaround. A dependency no longer fits the direction of the platform. The code still runs, but every new feature pays rent to an architectural decision made years ago. Replacing it is risky. Keeping it is risky too.</p><p>Matt Massicotte&#8217;s post sits nearby, although the debt is human rather than technical. He lists the open-source projects he can no longer maintain properly and asks people to take them over. I appreciated how direct the post is. Open source has plenty of launch energy and very little succession planning. We talk about adoption, stars, and contributors; we talk much less about what happens when the person whose name is on the repository no longer has the time to be its unpaid support team.</p><p>Asking for maintainers is not abandoning a project. Done early enough, it may be the most responsible maintenance decision available.</p><p>Irving Popovetsky&#8217;s post on iOS background tasks is the kind of article that only gets written after the happy path has stopped being useful. He instrumented <code>BGTaskScheduler</code> on his own device and watched what happened over time: refreshes arrived hours later than requested, resubmitting could accidentally push a pending task further into the future, and background launches became less frequent when the app went unopened.</p><p>The useful part is not a magic configuration that makes iOS behave like <code>cron</code>&#8212;there isn&#8217;t one. It is the record of all the small decisions required to make an unreliable system reliable enough: keeping refresh work short, handling expiration races, anchoring the next request to the last run, and being honest with users about what the operating system controls. This is maintenance in its purest form. The API call is easy; learning how it behaves after weeks of real use is the job.</p><p>And sometimes the maintenance problem is outside your repository. Jeff Johnson&#8217;s latest App Store post looks at suspicious gaps between ratings and written reviews, along with developers submitting apps at remarkable volume. You can improve your architecture, tests, and release process and still depend on a distribution system whose failures you cannot patch.</p><p>So I kept coming back to Mohammad Faani&#8217;s decision framework, especially his 70% rule: when one option looks roughly 70% right, choose it and adjust as you learn. That is useful advice for leads, but it is also a fair description of maintenance work. Most choices arrive with incomplete information. Wait for certainty and the backlog continues making decisions for you.</p><p>Working on SDKs has changed what I count as a successful release. A launch can be exciting, but the work that builds trust happens afterward: integrations continue working, old assumptions are tested against new platform behavior, and users do not need to know how much changed underneath them. &#8220;Nothing broke&#8221; is not much of a headline. It is still an outcome worth being proud of.</p><p>There is no tidy lesson here. Sometimes the right call is a migration. Sometimes it is one more compatibility fix. Sometimes it is admitting that a project needs a new maintainer. The hard part is noticing when the decision has changed&#8212;and making it before neglect makes it for you.</p><p><strong>Hit reply and tell me: what are you still sustaining years after you shipped it, and would you make the same choices again?</strong></p><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><h3><a href="https://danielsaidi.com/blog/2026/08/20/wally-7">Wally 7</a></h3><p>Daniel Saidi releases version 7 of Wally, the wallet app he first built in 2012. The biggest change is underneath: Realm is gone, SwiftData is in, and the new storage layer removes a lot of code while adding private iCloud sync.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.massicotte.org/blog/looking-for-maintainers/">Looking for Maintainers!</a></h3><p>Matt Massicotte has more open-source projects than he can properly support, so he names them and asks for new maintainers. A candid post about ownership, handoffs, and the part of open source that begins after people depend on your work.</p><p><strong>&#128205;Level: Beginner</strong></p><h3><a href="https://calcopilot.app/blog/posts/running-ios-background-tasks-reliably-part1/?utm_source=chatgpt.com">Running iOS Background Tasks Reliably, Part 1</a></h3><p>Irving Popovetsky instrumented <code>BGTaskScheduler</code> on a real device and documented what actually affects background execution: task expiration, accidental rescheduling, energy budgets, force-quitting, and how foreground usage changes the system&#8217;s willingness to wake an app. A valuable field report on building around behavior iOS deliberately does not guarantee.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://mfaani.com/posts/career/a-framework-to-make-decisions-faster-as-a-lead-software-engineer/">A Framework to Make Decisions Faster as a Lead Software Engineer</a></h3><p>Mohammad Faani offers a practical framework for decisions that cannot wait for perfect information: separate reversible choices from irreversible ones, timebox the research, write down the uncertainty, and use the 70% rule when it is time to commit.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://lapcatsoftware.com/articles/2026/8/9.html">More Incredible Tales of App Store Curation</a></h3><p>Jeff Johnson continues documenting App Store curation problems, including suspicious rating patterns and developers shipping apps at astonishing volume. A reminder that keeping an app healthy also means living with systems you do not control.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://swiftpackageindex.com/blog/what-is-a-package-registry">What Is a Swift Package Registry?</a></h3><p>Dave Verwer explains the difference between SwiftPM, a package index, and a package registry&#8212;and why verified, immutable source archives can be faster and safer than fetching every dependency through Git.</p><p><strong><span>&#128205;Level: </span>Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187;</h1><h3><a href="https://azamsharp.com/2026/08/22/protecting-swiftui-views-with-authentication.html">Protecting SwiftUI Views With Authentication</a></h3><p>A reusable <code>RequiresAuthentication</code> container for gating SwiftUI screens behind login, including inline and sheet-based flows, dismissal handling, and a view-modifier alternative. One important caveat is included too: hiding a screen does not replace server-side authorization.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://kylebrowning.com/posts/swiftui-empty-states-contentunavailableview/">Empty States in SwiftUI with ContentUnavailableView</a></h3><p>Kyle Browning walks through the <code>ContentUnavailableView</code> initializers and builds a reusable approach for search results, errors, offline screens, and other empty states.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://damian.fyi/swift/2026/08/16/curious-case-of-missing-click.html">The Curious Case of the Missing SwiftUI Clicks</a></h3><p>Buttons inside a bidirectional <code>ScrollView</code> stop responding after a few taps. Switching to vertical-only scrolling fixes the problem, although the reason remains unexplained. One to remember for the next impossible-looking SwiftUI bug.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://adincebic.com/2026/08/23/preventing-transitive-swift-imports-with.html">Preventing Transitive Swift Imports with Bazel</a></h3><p>Swift permits a module to import dependencies it never declared directly. Adin &#262;ebi&#263; shows how the <code>rules_swift</code> layering check makes those accidental imports fail at build time, before they become part of the architecture.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://antongubarenko.substack.com/p/ios-27-statereporter">iOS 27: StateReporter</a></h3><p>Anton Gubarenko explores StateReporting, a new iOS 27 framework that attaches app-state context to MetricKit and Instruments. Instead of seeing only that a hang occurred, you can see what the app was doing when it happened.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://livsycode.com/uikit/nstexttable-in-swift/">NSTextTable in Swift</a></h3><p>iOS 27 brings <code>NSTextTable</code>, <code>NSTextTableBlock</code>, and related TextKit APIs to UIKit, making real tables possible inside <code>NSAttributedString</code>. It is meant for rich text&#8212;documents, invoices, reports&#8212;not as a replacement for <code>UITableView</code>.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/news/?id=gmws0jgp">Changes for Apps in the European Union</a></h3><p>Apple announced new EU business terms taking effect October 1. The update replaces the per-install Core Technology Fee with a 5% commission on digital transactions in apps distributed outside the App Store, revises other commission rates, expands alternative-distribution eligibility, and adds child-safety rules for alternative payments.</p><h3><a href="https://www.swift.org/blog/embedded-swift-improvements-coming-in-swift-6.4/">Embedded Swift Improvements Coming in Swift 6.4</a></h3><p>Swift.org previews broader language support for Embedded Swift, including generalized existential types, untyped throws, and metatypes, plus floating-point parsing and error handling for concurrent operations on constrained targets.</p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://peterfriese.dev/blog/2026/hybrid-ai-apple-foundation-models-gemini">Apple Foundation Models: Hybrid AI with Dynamic Profiles</a></h3><p>Peter Friese builds a router that keeps smaller prompts on Apple&#8217;s on-device model and sends larger ones to Gemini. Dynamic Profiles hold the model choice, token-budget check, and model-specific instructions so the call site stays clean.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://cuteios.dev/2026/08/17/localizations-translations/">Translation Sub-Agents Go Brrrrrrrr</a></h3><p>Amy Delves shares a localization workflow built around <code>LocalizedStringResource</code>, a strings catalog for each view, detailed translator comments, and Xcode 27&#8217;s translation skill. It is a useful example of shaping a codebase so both people and coding agents have the context they need.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://fatbobman.com/en/weekly/issue-150/?utm_source=chatgpt.com">AI Wanted to Give Up. The Human Didn&#8217;t.</a></h3><p>Fatbobman reflects on Linus Torvalds&#8217;s &#8220;debug session from hell&#8221;: 24 debugging patches and 18 kernel boots to find a one-line fix. AI helped with the repetitive work, but human judgment supplied the persistence. A timely reminder that faster tools do not remove the hardest part of debugging&#8212;knowing when the evidence still does not add up..</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Tutorials &#128210;</h1><h3><a href="https://tanaschita.com/swiftui-alert-identifiable-data/">Presenting Alerts and Confirmation Dialogs from Identifiable Data in SwiftUI</a></h3><p>Natascha Fadeeva demonstrates the new iOS 27 item-based APIs for presenting alerts and confirmation dialogs. Binding presentation directly to an optional <code>Identifiable</code> value removes the separate Boolean state&#8212;and the synchronization bugs that can come with it.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Video&#127909;</h1><h3><a href="https://www.youtube.com/watch?v=oatrwczkdco">AI-Assisted TDD in Practice (Swift + AI)</a></h3><p>Quality Coding demonstrates how test-driven development can put useful boundaries around AI-generated Swift code. The tests define the expected behavior, while the coding assistant works toward small, verifiable changes instead of producing an entire implementation at once.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Devs for Devs: Short Eye Long Eye]]></title><description><![CDATA[From world-class developers, straight to you.]]></description><link>https://thosewhoswift.substack.com/p/devs-for-devs-short-eye-long-eye</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/devs-for-devs-short-eye-long-eye</guid><pubDate>Sun, 23 Aug 2026 20:51:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello! &#128075;</p><p>Another Sunday, another &#8220;<em>Devs for Devs</em>&#8221; issue!</p><p>Meet <strong>Faiq Khan</strong> , an experienced iOS engineer with a strong background in building and shipping mobile products.</p><p>Today, he answers a simple question many indie devs face: <em><strong>what should an indie dev keep in mind while developing?</strong></em></p><p>With his years of hands-on development experience and practical knowledge from working across different projects and teams, he shares his tips on how to keep moving forward.</p><p>P.S. You can check out and follow Faiq&#8217;s work on <strong><a href="http://linkedin.com/in/faiq-khan-42072677">LinkedIn</a></strong> and <strong><a href="https://www.faiq.co.uk/">website</a></strong>.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fkOT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!fkOT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!fkOT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!fkOT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fkOT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:313679,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/211269758?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fkOT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!fkOT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!fkOT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!fkOT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0638bc7-baab-482e-948d-95d9c4c30742_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!X8JC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!X8JC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!X8JC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!X8JC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!X8JC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!X8JC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg" width="1408" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!X8JC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg 424w, https://substackcdn.com/image/fetch/$s_!X8JC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg 848w, https://substackcdn.com/image/fetch/$s_!X8JC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!X8JC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19cefb79-2c9b-48e9-a57a-2c64ef7c4223_1408x768.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>A Strategy for Indie Developers</h3><p><span>As indie developers, we lack the safeguarding structure that full-sized product teams enjoy. We don&#8217;t have a dedicated PM, a scrum master, or the formal structure of a sprint or squad environment, systems designed to handle heavy planning, detailed estimation, and peer discussion.</span></p><p><span>You are often completely on your own making those decisions.</span></p><p><span>While traditional agile processes can work, as indie devs we need to relentlessly streamline how much energy we assign to planning overhead in order to deliver features in a reasonable time.</span></p><p><span>It&#8217;s easy to get caught up thinking about the distant product we want to build:</span></p><p><span>The big vision.</span></p><p><span>The polished UI.</span></p><p><span>The features we haven&#8217;t even started yet.</span></p><p><span>The version that exists somewhere months down the road.</span></p><p><span>Having that long-term vision is important. It gives you direction and something to work towards.</span></p><p><span>But there&#8217;s a danger in staring at the destination for too long: </span><strong><span>you can lose sight of what actually needs to happen today.</span></strong></p><p><span>I&#8217;ve found it much more useful to think in two layers:</span></p><p><strong><span>Long Eye:</span></strong><span> Where am I ultimately trying to take this product?</span></p><p><strong><span>Short Eye:</span></strong><span> What specifically needs to happen today to move me one step closer?</span></p><p><span>And that short-term vision should get </span><em><span>really</span></em><span> short.</span></p><p><span>Not </span><em><span>&#8220;build the new creation system this week.&#8221;</span></em></p><p><span>Instead:</span></p><p><em><span>&#8220;Today, get the item selection working.&#8221;</span></em></p><p><span>Tomorrow might be:</span></p><p><em><span>&#8220;Connect the selected item to the inventory system.&#8221;</span></em></p><p><span>Small, achievable steps.</span></p><p><span>There&#8217;s another benefit to working this way: </span><strong><span>small steps make it harder to fall down rabbit holes.</span></strong></p><p><span>We&#8217;ve all been there. You start working on one feature, notice something that could be improved, start refactoring it, discover another problem, research a new approach&#8230; and suddenly three hours have disappeared and you&#8217;re nowhere near what you originally set out to do.</span></p><h3><span>Daily Goals</span></h3><p><span>A clearly defined daily goal gives you something to anchor yourself to.</span></p><p><span>You can still explore. You can still experiment. But you always have a destination for the day.</span></p><p><span>For me, a good daily goal should be (</span><em><span>MASS</span></em><span>):</span></p><p><strong><span>Meaningful</span></strong><span>:  it should move the product forward, even if only slightly.</span><strong><span><br>Achievable</span></strong><span>: something I genuinely have time to complete.</span></p><p><strong><span>Simple</span></strong><span>:  clear enough that I don&#8217;t spend half the day deciding what to do.</span></p><p><strong><span>Small</span></strong><span>: scoped to fit within the day.</span></p><p><span>And there&#8217;s one task I always want to finish the day with:</span></p><p><strong><span>Review tomorrow&#8217;s agenda.</span></strong></p><p><span>Before closing the laptop, decide what tomorrow&#8217;s small win looks like.</span></p><p><span>That way, when you sit down the next morning, you&#8217;re not asking:</span></p><p><em><span>&#8220;Where do I start?&#8221;</span></em></p><p><span>You already know.</span></p><p><span>The </span><strong><span>Long Eye</span></strong><span> tells you where you&#8217;re going.</span></p><p><span>The </span><strong><span>Short Eye</span></strong><span> tells you what to do next.</span></p><p><span>Keep the steps small, stay out of the rabbit holes, and keep moving forward.</span></p><p><strong><span>Think big. Plan small. Ship every day.</span></strong></p>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 280]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-280</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-280</guid><pubDate>Wed, 19 Aug 2026 20:31:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>This week&#8217;s articles share a common thread: frameworks can change significantly beneath APIs that look exactly the same.</p><p>SwiftUI&#8217;s compiler performance is improving, <code>@State</code> has a new underlying implementation, and developers are still debating whether the framework&#8217;s long-standing limitations are acceptable. Together, these pieces show why understanding the mechanism&#8212;not just the API&#8212;often leads to faster debugging and better engineering decisions.</p><p>Have you ever had to investigate a SwiftUI or UIKit behavior that the documentation couldn&#8217;t explain? Hit reply&#8212;I&#8217;d love to hear what you found.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210653241?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h1>Under SwiftUI's Hood, Nothing Is As Simple As It Looks</h1><p><em>with Divya Ravi &#8212; Technical Editor, iOS engineer</em></p><p>Seven years after its introduction, SwiftUI still asks developers to trust a considerable amount of hidden machinery. This week&#8217;s reading made a strong case for looking beneath that abstraction.</p><p>Start with <code>ContentBuilder</code>. Apple introduced it at WWDC26 as a way to improve SwiftUI type-checking performance. But as Fatbobman explains, the improvement does not come from adding another builder type. It comes from restructuring the shared builder APIs used by containers such as <code>Group</code> and <code>ForEach</code>.</p><p>By relying on <code>TupleContent</code> and deferred conditional conformance, SwiftUI gives the type checker less work to perform. The code developers write remains largely unchanged, while the compiler does substantially less work behind the scenes.</p><p>That distinction matters. When build performance improves without an obvious source change, it is easy to treat it as compiler magic. Understanding the implementation gives us something more useful: a clearer mental model for why certain view structures are expensive and why newer APIs may compile faster.</p><p>A similar change is happening with <code>@State</code>.</p><p>Stewart Lynch found that in Xcode 27, <code>@State</code> is implemented as a macro rather than a property wrapper. The syntax remains familiar, but the initialization behavior changes in an important way.</p><p>Previously, the initializer expression could be evaluated during every view recreation, even when SwiftUI ultimately preserved the existing state value. With the macro implementation, that expression is evaluated lazily.</p><p>For a simple integer or Boolean, the difference is unlikely to matter. For state initialized through a database lookup, file operation, or expensive computation, it can eliminate work that developers may not have realized was happening.</p><p>These two changes approach the same problem from different directions. <code>ContentBuilder</code> improves compiler behavior beneath a stable API, while the new <code>@State</code> implementation changes the underlying mechanism without changing how the declaration looks. Both improvements are nearly invisible unless someone profiles the behavior, examines the generated code, or asks why the result changed.</p><p>That brings us to Yakov Manshin&#8217;s &#8220;SwiftUI After 7 Years: A Story of Mediocrity,&#8221; a deliberately sharp retrospective that generated more than 300 comments on Hacker News.</p><p>His central argument is difficult to dismiss completely: after seven years, &#8220;it is still a young framework&#8221; becomes a less convincing explanation for persistent problems. Focus management, drag-and-drop, platform inconsistencies, and difficult edge cases have survived several release cycles.</p><p>I do not agree with every conclusion in the article, but the larger tension is real. SwiftUI&#8217;s internals continue to improve, while some visible limitations remain stubbornly familiar.</p><p>Those are separate measures of maturity. Compiler performance can improve dramatically while developers still struggle with basic interactions. A framework can gain powerful new APIs without resolving gaps in older ones. When we say SwiftUI has come a long way, it is worth being precise about which part of SwiftUI we mean.</p><p>Chang-Jui Lin&#8217;s article on building an EPUB reader with CoreText offers a practical counterpoint. Instead of adapting a WebView or relying on a higher-level text component, Lin built directly on CoreText to support reflowable content, fixed layouts, media overlays, and bidirectional text.</p><p>It is a much lower level of abstraction&#8212;and a much larger engineering commitment. But it also provides the control required for a problem that higher-level frameworks do not solve cleanly.</p><p>That is not an argument for rebuilding every framework primitive. Most abstractions are valuable precisely because they prevent us from owning that complexity. But when an abstraction becomes the constraint, understanding the layer below it can reveal options that are otherwise easy to miss.</p><p>The broader lesson from this week is not that SwiftUI is good or bad. It is that no framework should remain a complete black box.</p><p>The engineers who diagnose difficult problems fastest are often the ones willing to inspect the generated code, profile the rebuild, trace the macro expansion, or move down the stack when the current abstraction stops being useful.</p><p>Frameworks will continue to improve while retaining frustrating gaps&#8212;sometimes within the same release. The durable skill is learning how to investigate what the framework is actually doing. That understanding makes debugging faster, design decisions clearer, and point-release surprises far less surprising.</p><p>What is the last SwiftUI, UIKit, or Swift behavior you had to reverse-engineer because the documentation did not explain enough?</p><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><h3><a href="https://fatbobman.com/en/posts/contentbuilder-explained/">ContentBuilder Explained: The Secret Behind SwiftUI's Type-Checking Speedup</a></h3><p>An investigation into how Apple improved SwiftUI type-checking performance through <code>TupleContent</code>, shared builder APIs, and deferred conditional conformance. A useful read if you want to understand what changed beneath the WWDC26 performance claims.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://www.createchsol.com/blog/2026-08-12-a-change-of-state.html">A Change of State</a></h3><p>A focused demonstration of <code>@State</code> becoming a macro in Xcode 27. The new implementation lazily evaluates its initializer, avoiding unnecessary work when SwiftUI recreates a view.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://ykvm.com/2026/07/swiftui-a-story-of-mediocrity/">SwiftUI After 7 Years: A Story of Mediocrity</a></h3><p>A critical assessment of SwiftUI&#8217;s maturity and the framework limitations that remain after seven years. You may not agree with every argument, but it is a useful prompt to reconsider which rough edges we have started treating as normal.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://chang-jui-lin.github.io/Yuedu-reader/2026/06/08/coretext-epub3-adaptation/">Adapting EPUB 3 Features to CoreText in Yuedu Reader</a></h3><p>A detailed look at implementing EPUB 3 rendering directly with CoreText, including reflowable text, fixed layouts, media overlays, and bidirectional content. It shows both the control and the complexity involved in owning a native text-rendering pipeline.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://damian.fyi/swift/2026/08/16/curious-case-of-missing-click.html">The Curious Case of the missing SwiftUI Clicks</a></h3><p>A real-time debugging log of SwiftUI buttons that quietly stop responding inside a horizontal ScrollView &#8212; fixed, but still without a clear answer for <em>why</em>. A fitting closer for this week's theme: sometimes going under the hood just leaves you with a better mystery.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://toyboy2.medium.com/swiftui-lies-your-animation-breaks-because-one-view-is-actually-multiple-layers-56c1abdc1790">SwiftUI Lies: Your Animation Breaks Because One View Is Actually Multiple Layers</a></h3><p>A first-person debugging story showing why a button&#8217;s label and background can animate independently &#8212; and how ZStack, drawingGroup, compositingGroup, and geometryGroup each solve (or half-solve) the problem for different reasons. A concrete companion to this week&#8217;s &#8220;nothing is as simple as it looks&#8221; theme.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://mysk.blog/2026/08/04/webkit-proxy-icloud-private-relay-ip-leak/">IP and DNS Leaks in WebKit Affecting Proxy Browsers and Apple iCloud Private Relay</a></h3><p>Researchers document a WebKit issue that can expose a user&#8217;s real IP address and DNS queries while using proxy-based browsers or iCloud Private Relay. Important context for teams making privacy guarantees around WebKit-based experiences.</p><p><strong><span>&#128205;Level: </span>Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187;</h1><h3><a href="https://onmyway133.com/posts/how-to-stream-sse-with-urlsession-in-swift/">How to stream SSE with URLSession in Swift</a></h3><p>A practical implementation of Server-Sent Events using <code>URLSession.bytes(for:)</code>, along with a small test server. Particularly useful for apps consuming AI responses or other incrementally delivered data.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.pointfree.co/blog/posts/220-type-safe-json-and-jsonb-in-structuredqueries">Type-safe JSON and JSONB in StructuredQueries</a></h3><p>StructuredQueries 0.35 introduces schema-checked support for JSON and JSONB data in SQLite. The article shows how to retain type safety while working with semi-structured local data.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://swiftdevjournal.com/posts/top-functions/">Using Top Functions Mode in Instruments to Quickly Find the Slowest Code</a></h3><p>A concise guide to the Top Functions view in Xcode 26.4&#8217;s profiling instruments. Sorting by self-weight and inspecting flame graphs can make it much easier to identify where execution time is actually being spent.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.donnywals.com/installing-simulator-runtimes-from-the-command-line/">Installing simulator runtimes from the command line</a></h3><p>Command-line steps for installing simulator runtimes when Xcode 27 beta remains stuck on &#8220;fetching download information.&#8221; A useful workaround to keep nearby when the graphical installer fails.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.avanderlee.com/swift/sendable-protocol-closures/">Sendable and @Sendable closures explained with code examples</a></h3><p>A clear explanation of <code>Sendable</code> types and <code>@Sendable</code> closures, with examples showing how values move safely across concurrency boundaries. A solid reference when working through Swift 6 strict-concurrency warnings.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://swiftwithmajid.com/2026/06/23/taking-control-of-toolbar-items-in-swiftui/">Taking control of toolbar items in SwiftUI</a></h3><p>Explores SwiftUI toolbar placement, customization, and platform-specific behavior. Helpful for interfaces where the default toolbar layout does not provide enough control or consistency.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/news/?id=kug6m2ea">Get ready for new creative assets on the App Store</a></h3><p>Apple previews new creative and marketing asset options for App Store product pages. Worth reviewing before planning your next product-page update or launch campaign.</p><h3><a href="https://developer.apple.com/help/app-store-connect/release-notes/">TestFlight Update</a></h3><p>The latest TestFlight release notes, covering recent changes to Apple&#8217;s beta-distribution workflow. A quick check before your next external-testing cycle may save some surprises.</p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://www.polpiella.dev/cursor-xcode-27">Using Cursor in Xcode 27</a></h3><p>A hands-on guide to connecting Cursor to Xcode 27 through Agent Client Protocol support. It offers an early look at how external coding agents can participate directly in an Xcode-based workflow.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://agent-island.dev/blog/codex-token-replay-guard/">Build a Local Codex Token Usage Ledger Without Replay Errors</a></h3><p>A practical fix for tracking token usage across Codex/Claude coding-agent sessions without hitting replay errors &#8212; useful if AI agents are part of your build pipeline, even outside the Swift/iOS-specific tooling this newsletter usually covers.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Tutorials &#128210;</h1><h3><a href="https://www.basbroek.nl/recording-voiceover">Recording VoiceOver on iOS &amp; macOS</a></h3><p>A practical guide to capturing VoiceOver audio for accessibility testing, bug reports, and demos. iOS supports the workflow through screen recording, while macOS requires a virtual-audio device.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Community&#129309;</h1><h3><a href="https://macguru.dev/swiftui-should-become-open-source/">SwiftUI should become Open-Source</a></h3><p>Makes the case that open-sourcing SwiftUI could help developers diagnose and address long-standing issues such as keyboard focus and drag-and-drop. Even if the proposal feels unlikely, the argument raises useful questions about framework transparency and community participation.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Devs for Devs: From XCUITest to Promo Video]]></title><description><![CDATA[From world-class developers, straight to you.]]></description><link>https://thosewhoswift.substack.com/p/devs-for-devs-from-xcuitest-to-promo</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/devs-for-devs-from-xcuitest-to-promo</guid><pubDate>Sun, 16 Aug 2026 19:30:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Another Sunday, another gem from top developers for you!</p><p>This week, our guest author is Noam Efergan, a Senior Mobile Engineer at Yazio specializing in iOS development.</p><p>A self-taught developer based in the UK, he&#8217;s also a technical writer with a particular interest in CI/CD, automation, and modern mobile development practices.</p><p>Beyond his engineering work, Noam is a core member of the SwiftLeeds team and an active contributor to the iOS community, sharing his experience through articles, conference talks, and open discussions about mobile engineering.</p><p>Let&#8217;s see how a coding task can lead to a marketing video matching the latest updates.</p><p>P.S. You can check out Noam&#8217;s work on <strong><a href="https://www.linkedin.com/in/noamefergan/">LinkedIn</a></strong>, <strong><a href="https://x.com/No_Wham">X</a></strong>, and <strong><a href="https://nowham.dev">website</a></strong>.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hSbB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!hSbB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!hSbB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!hSbB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hSbB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:327430,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210577607?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hSbB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!hSbB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!hSbB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!hSbB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff529db27-3607-4d3a-836c-3ce3276d2482_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><h2><span>Treating Marketing Assets Like Build Artifacts</span></h2><p><span>Every time I made a meaningful change to Dee&#8217;s interface, I created a second task for myself: remake the marketing.</span></p><p><span>A label moved, a timer changed, or a new state appeared. Suddenly an App Store screenshot was stale, a clip no longer matched the product, or a translated screen had to be recorded again. Recreating the right state by hand was often slower than the UI change itself. Timers kept moving, animations settled at different moments, and sample data was never quite identical.</span></p><p><span>That is a familiar indie developer problem. The person building the product is also producing the screenshots, launch posts, and promo videos. Repeating fragile setup work is a poor use of that person&#8217;s time.</span></p><p><span>I eventually stopped treating marketing assets as files I created manually. I started treating them as build artifacts that should be reproducible from the real app.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xSxM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xSxM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png 424w, https://substackcdn.com/image/fetch/$s_!xSxM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png 848w, https://substackcdn.com/image/fetch/$s_!xSxM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png 1272w, https://substackcdn.com/image/fetch/$s_!xSxM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xSxM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png" width="430" height="934.4055201698513" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2047,&quot;width&quot;:942,&quot;resizeWidth&quot;:430,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A deterministic capture of Dee's real workout screen&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A deterministic capture of Dee's real workout screen" title="A deterministic capture of Dee's real workout screen" srcset="https://substackcdn.com/image/fetch/$s_!xSxM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png 424w, https://substackcdn.com/image/fetch/$s_!xSxM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png 848w, https://substackcdn.com/image/fetch/$s_!xSxM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png 1272w, https://substackcdn.com/image/fetch/$s_!xSxM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff24f60f-ac8e-4de3-8993-bfe75be14753_942x2047.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>The raw source is the real SwiftUI workout screen, launched with controlled data and time</em></figcaption></figure></div><h2><span>Start with an Exact SwiftUI State</span></h2><p><span>Dee is a Muay Thai training app with time-based workout screens. That makes manual capture especially awkward. By the time I navigate to the correct round, dismiss the right controls, and start recording, the timer has already changed.</span></p><p><span>The solution begins inside the app. In debug builds, a launch argument selects a named marketing state. Each state creates the same typed models used by the product, but with controlled input. A frozen clock stops the timer from drifting while XCTest waits for the interface.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;rust&quot;,&quot;nodeId&quot;:&quot;19d6f3c0-0e8a-4a54-96ad-aaeed9fd9753&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-rust">#if DEBUG
enum MarketingScreenshotSeed {
    static let launchArgument = &#8220;-AppStoreScreenshotMode&#8221;

    enum Mode: String, CaseIterable, Sendable {
        case roundTimer = &#8220;round-timer&#8221;
        case restNext = &#8220;rest-next&#8221;
        case liveActivity = &#8220;live-activity&#8221;
    }

    static func makeClock() -&gt; TestClock {
        TestClock(now: Date(timeIntervalSince1970: 1_800_000_000))
    }
}
#endif</code></pre></div><p><span>The important part is not the particular launch argument. It is the boundary. Marketing fixtures control dependencies and data, but they do not replace the production view with a separate mock screen. If I change the actual workout layout, the next capture contains that change.</span></p><p><span>Keeping the fixtures debug-only also prevents this tooling from becoming a hidden route through the release app.</span></p><h2><span>Let XCUITest Operate the Camera</span></h2><p><span>Once the app can launch directly into a stable state, XCUITest becomes a reliable camera operator. The test starts the requested mode and locale, waits for the real screen, and stores the screenshot as a named attachment.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;1761fc0f-91ad-4093-aea4-3db989d2b85d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">private func captureMarketingLocale(_ locale: String) {
    for (name, mode) in marketingScreens {
        let app = launchWorkout(locale: locale, mode: mode)
        settle(seconds: 1.5)

        let attachment = XCTAttachment(screenshot: app.screenshot())
        attachment.name = &#8220;\(locale)_\(name)&#8221;
        attachment.lifetime = .keepAlways
        add(attachment)

        app.terminate()
    }
}</code></pre></div><p><span>I currently run the capture flow across six locales. Locale is just another launch input, so translated screens follow the same path as English instead of becoming a separate manual checklist.</span></p><p><span>Animations introduced another source of flakiness. A view can be accessible before the simulator has produced a complete frame. For the more animated screens, the test samples screenshots until it receives the same PNG twice. If the compositor never settles, the test fails rather than quietly exporting a broken image.</span></p><p><span>That failure is useful. When a UI change invalidates a fixture or makes a screen unstable, I find out during capture. I do not discover it weeks later while assembling launch material from an old folder.</span></p><h2><span>Move from XCTest to Remotion</span></h2><p><span>The XCTest attachments are extracted from the result bundle and copied into a small marketing workspace. From there, Remotion turns the real app captures into code-driven vertical video.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;83fdf0b4-9c15-463f-8dc9-3111e3bdcc81&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">&lt;AbsoluteFill&gt;
  &lt;TransitionSeries&gt;
    &lt;TransitionSeries.Sequence durationInFrames={120}&gt;
      &lt;FastHookScene screenshotPath={roundScreenshotPath} /&gt;
    &lt;/TransitionSeries.Sequence&gt;
    &lt;TransitionSeries.Transition
      presentation={slide({direction: &#8220;from-right&#8221;})}
      timing={springTiming({durationInFrames: 15})}
    /&gt;
    &lt;TransitionSeries.Sequence durationInFrames={150}&gt;
      &lt;FastProofScene
        nextScreenshotPath={nextScreenshotPath}
        summaryScreenshotPath={summaryScreenshotPath}
      /&gt;
    &lt;/TransitionSeries.Sequence&gt;
  &lt;/TransitionSeries&gt;

  {deeCueAudioPath ? (
    &lt;Sequence from={105} durationInFrames={135}&gt;
      &lt;Audio src={staticFile(deeCueAudioPath)} /&gt;
    &lt;/Sequence&gt;
  ) : null}
  &lt;TimedCaptions captions={captions} /&gt;
&lt;/AbsoluteFill&gt;</code></pre></div><p><span>The composition controls timing, transitions, captions, framing, and audio. The current example renders at 1080 by 1920, 30 frames per second, and 12 seconds. It uses Dee&#8217;s captured screens, and the composition can include a cue copied from the app&#8217;s shipped resources rather than recreating the interface or sound for the advertisement.</span></p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;4ddbc074-94c1-4fdb-88ad-9d40f11a2867&quot;,&quot;duration&quot;:null}"></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mDf6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mDf6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png 424w, https://substackcdn.com/image/fetch/$s_!mDf6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png 848w, https://substackcdn.com/image/fetch/$s_!mDf6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png 1272w, https://substackcdn.com/image/fetch/$s_!mDf6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mDf6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png" width="1456" height="502" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:502,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The deterministic marketing pipeline&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The deterministic marketing pipeline" title="The deterministic marketing pipeline" srcset="https://substackcdn.com/image/fetch/$s_!mDf6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png 424w, https://substackcdn.com/image/fetch/$s_!mDf6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png 848w, https://substackcdn.com/image/fetch/$s_!mDf6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png 1272w, https://substackcdn.com/image/fetch/$s_!mDf6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F523dedee-6ea0-4ad2-81f6-c94124011b4d_1800x620.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>The product produces the evidence. The marketing layer selects, composes, and validates it.</em></figcaption></figure></div><p><span>I also added a capture manifest and hashes around the process. The manifest binds the exact screenshots, audio, and composition properties used by a render. Preflight checks dimensions, captions, asset provenance, and whether inputs changed after capture. Human review remains the final step.</span></p><p><span>This is more machinery than a single promo clip needs. It becomes valuable because the output is recurring. One source capture can support App Store screenshots, social video, carousels, and localized variants without pretending that a design mock is the shipping product.</span></p><h2><span>What was Worth Automating</span></h2><p><span>Coding agents helped me move faster through repetitive plumbing, test cases, and review. They were useful for tracing asset paths and checking consistency across the Swift and TypeScript sides. They did not decide what the product should claim, which screen represented it honestly, or whether the final creative was ready to publish. Those decisions stayed with me.</span></p><p><span>The pipeline still has a maintenance cost. Fixtures must evolve with the app. UI tests need care around animations and simulator behaviour. Remotion adds another toolchain. If I needed one screenshot for one launch, I would take it manually.</span></p><p><span>I would consider this approach when an app has dynamic screens, several locales, frequent UI changes, or an ongoing need for product content. Start with one painful state, not a complete marketing platform. Add a launch argument, freeze its dependencies, capture it in a UI test, and only automate the next stage when repetition justifies it.</span></p><p><span>The larger lesson for me is that testability has uses beyond tests. When an app can reproduce an exact state on demand, it becomes easier to debug, demonstrate, localize, and market. The best part of this workflow is not that it produces a video. It is that the video remains connected to the product that users will actually open.</span></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for FREE to receive Sunday&#8217;s gems from devs and Wednesday&#8217;s latest news. &#128071;</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 279]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-279</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-279</guid><pubDate>Wed, 12 Aug 2026 20:30:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>Swift concurrency stopped being the scary new thing a while ago &#8212; it&#8217;s just how we write Swift now. But &#8220;we adopted it&#8221; and &#8220;we understand it&#8221; are different claims, and this week&#8217;s picks live in the gap between them. The theme: concurrency that&#8217;s grown past the tutorials and into the parts of real apps where the edges actually are.</p><p>Wrestling with an isolation error that makes no sense? Reply &#8212; those are the best ones to dig into together.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:382418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210653241?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!psG3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!psG3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!psG3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c13ffd8-7b88-485e-be23-b7d8b9f79ef8_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h1>The Delegate Is Dead. Long Live the Actor.</h1><p><em>with Divya Ravi &#8212; Technical Editor, iOS engineer</em></p><p>Every Apple framework of a certain age carries the fingerprints of the era it was born in. Open CoreBluetooth, or CoreLocation, or any of the old guard, and you&#8217;re not just reading an API &#8212; you&#8217;re reading 2011. The delegate pattern everywhere. State machines you assemble by hand across six callbacks that fire in an order the compiler knows nothing about. A &#8220;did&#8221; method for every possible thing that could happen, and a strong-reference dance you have to get exactly right or the whole thing quietly deallocates out from under you.</p><p>For a decade, that was just the cost of doing business on Apple platforms. We learned it, we internalized it, and we mostly stopped noticing how strange it was. You want to read one value off a Bluetooth sensor? Sixty lines, seven delegate methods, and a comment somewhere that says <code>// now what?</code> for the disconnect case nobody fully handled.</p><p>What&#8217;s happening in Swift right now is the slow, deliberate dismantling of that world &#8212; and I don&#8217;t think we talk about it plainly enough. Structured concurrency didn&#8217;t just give us a nicer way to write async code. It changed what the <em>shape</em> of correct code looks like. The delegate era scattered your logic across callbacks because callbacks were the only tool for &#8220;tell me when this happens later.&#8221; Async/await collapsed that back into something you can read top to bottom: wait for the radio, find the device, connect, stream the readings. The program finally looks like the sentence you&#8217;d use to describe it.</p><p>But here&#8217;s the part I actually want to make the case for, because it&#8217;s subtler than &#8220;async is nicer.&#8221; The real shift isn&#8217;t syntax. It&#8217;s <em>who enforces correctness</em>. In the delegate world, thread-safety was a promise you made to yourself in a comment &#8212; &#8220;these callbacks arrive on this queue, so be careful.&#8221; Nobody checked. You just had to be right, every time, forever, including the time six months from now when a tired version of you touches that file at 4pm on a Friday. Swift 6&#8217;s isolation model takes that promise and hands it to the compiler. The actor doesn&#8217;t ask you to be careful about which context your state is touched from. It makes touching it from the wrong context <em>not compile.</em></p><p>That is a genuinely different relationship with your own code. And it&#8217;s why the isolation errors everyone complains about are, I&#8217;d argue, mostly a good thing wearing an annoying costume. When the compiler rejects your code with some knotty message about a non-Sendable type crossing an actor boundary, it is usually not being pedantic. It&#8217;s telling you about a data race that, in the old world, would have shipped &#8212; and surfaced three weeks later as a one-in-ten-thousand crash you could never reproduce. The friction moved earlier. That&#8217;s the whole trade. You pay at compile time instead of paying at 2am when the pager goes off.</p><p>The catch &#8212; and there&#8217;s always a catch &#8212; is that the model is subtle exactly where it matters most. Protocols and global actors interact in ways that surprise even people who&#8217;ve been doing this for years. The difference between isolating a protocol, a conformance, and a requirement is real, and getting it wrong produces errors that feel arbitrary until the mental model clicks. That&#8217;s the actual work of this era: not memorizing which keyword goes where, but building an intuition for what the compiler is protecting you from, so the errors read as help instead of harassment.</p><p>So if you&#8217;ve been treating Swift concurrency as a box you check &#8212; &#8220;yes, we migrated, yes, it compiles&#8221; &#8212; I&#8217;d gently push on that. The migration is the easy part. The understanding is the part that pays off, because it&#8217;s the difference between fighting the compiler and letting it do the thing it&#8217;s uniquely good at: proving, before your code ever runs, that an entire category of bug simply cannot happen.</p><p>The delegate taught us to be careful. The actor lets us stop having to be. That&#8217;s not a small upgrade. That&#8217;s the ground moving.</p><p>What concurrency concept finally clicked for you &#8212; and what&#8217;s still fighting you? Hit reply. The best answers make next week&#8217;s issue.</p><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><p>Concurrency past the tutorials &#8212; where the real edges are.</p><h3><a href="https://kylebrowning.com/posts/bluetooth-without-the-delegate-dance/">Bluetooth Without the Delegate Dance</a></h3><p>Kyle Browning rebuilds CoreBluetooth for the Swift 6.2 era &#8212; writing the same heart-rate feature three ways (raw delegates, a 2021 wrapper, and a modern actor-based design) to show exactly how much the language moved. The payoff: tying an actor's isolation directly to CoreBluetooth's delivery queue via custom executors, so events arrive already isolated. A masterclass.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://www.massicotte.org/blog/protocols-and-global-actors/">An Even Closer Look at Protocols and Global Actors</a></h3><p>Matt Massicotte on the genuinely subtle interaction between protocols and global actors &#8212; the difference between isolating a protocol, a conformance, and a requirement, and why getting it wrong produces errors that feel arbitrary until the model clicks.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://blakecrosley.com/blog/swift-6-2-concurrency-in-practice">Swift 6.2 Concurrency in Practice: Default to MainActor, Escape on Purpose</a></h3><p>Blake Crosley on the mental-model flip in Swift 6.2 &#8212; default your module to the main actor, then leave it on purpose in named places. The same data-race guarantee, far fewer false-positive errors, drawn from shipping it across a real app family.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://azamsharp.com/2026/08/02/building-testable-swiftdata-apps.html">Building Testable SwiftData Apps</a></h3><p>Mohammad Azam on structuring SwiftData so it's actually testable &#8212; keeping persistence at arm's length so your logic runs in tests without a live store fighting you at every step.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://www.swift.org/blog/whats-new-in-swift-july-2026/">What's New in Swift: July 2026 Edition</a></h3><p>The official Swift.org digest rounds up the month across the project &#8212; releases, evolution proposals, community talks, and the news that Swift now runs in a broader range of IDEs including Cursor, VSCodium, Kiro, and Antigravity via the Open VSX Registry.</p><p><strong><span>&#128205;Level: Beginner</span></strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187;</h1><h3><a href="https://tanaschita.com/swiftui-focus-state/">Managing Focus in SwiftUI with FocusState</a></h3><p>Natascha Fadeeva on driving focus properly in SwiftUI &#8212; FocusState, programmatic focus changes, and the keyboard-management details that make forms feel right.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://sagarunagar.com/blog/swiftui-document-apis-2027-whats-new/">SwiftUI Document APIs in the 2027 Releases: Everything You Need to Know</a></h3><p>Sagar Unagar on the new Document protocol &#8212; snapshot-based updates, efficient disk access, and how it layers on top of the existing FileDocument world without breaking it.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://artemnovichkov.com/blog/using-swiftui-contentbuilder-with-non-view-types">Using SwiftUI's ContentBuilder with Non-View Types</a></h3><p>Artem Novichkov digs into ContentBuilder &#8212; the unified builder replacing per-container overloads &#8212; and how to use it with non-view types, plus what it means for compile times.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://antongubarenko.substack.com/p/ios-27-media-intelligence-framework">iOS 27: Working with the Media Intelligence Framework</a></h3><p>Anton Gubarenko digs into iOS 27&#8217;s new Media Intelligence framework &#8212; what it exposes, what it&#8217;s actually good for, and where it fits alongside the existing media APIs.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/hello/august26/">Hello, Developer: August 2026</a></h3><p>Apple's monthly developer digest for August &#8212; a guided tour of the biggest WWDC26 updates, a walkthrough of building App Store assets for iOS 27, and a profile of the team behind the hit game "Is This Seat Taken?"</p><div><hr></div><h1 style="text-align: center;">Design &#127912;</h1><h3><a href="https://alexanderweiss.dev/blog/2026-08-09-concentric-button-os-27-swiftui-apis">Concentric Buttons with OS 27's SwiftUI APIs</a></h3><p>Alexander Weiss on the new concentric-shape APIs in OS 27 &#8212; building nested, concentric button shapes that stay aligned the way the new design language expects.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://nilcoalescing.com/blog/AdaptiveSwiftUIToolbarsInIOS27/">Adaptive SwiftUI Toolbars in iOS 27</a></h3><p>Natalia Panferova on the new adaptive toolbar behavior in iOS 27 &#8212; how toolbars reflow across size classes and what you get for free versus what you still configure.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://swiftrocks.com/using-ai-without-losing-critical-thinking">Using AI Without Losing Your Critical Thinking</a></h3><p>Bruno Rocha on staying sharp while leaning on AI tools &#8212; a thoughtful, non-hype take on where agents genuinely help and where offloading judgment quietly costs you.</p><p><strong>&#128205;Level: Beginner</strong></p><h3><a href="https://mfaani.com/posts/ai/how-to-use-git-worktree-and-how-its-used-by-ai-agents/">How to Use Git Worktree &#8212; and How AI Agents Use It</a></h3><p>Mohammad Faani on git worktrees &#8212; the underused feature that lets you check out multiple branches at once, and why it's suddenly relevant as AI agents work across branches in parallel.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Video &#127909;</h1><h3><a href="https://www.amyworrall.com/blog/previewing-sf-symbols-with-apples-own-renderer">Previewing SF Symbols with Apple's Own Renderer</a></h3><p>Amy Worrall wires up Apple's own SF Symbols renderer to preview symbols exactly as the system draws them &#8212; a neat bit of tooling for anyone who's been squinting at approximations.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Devs for Devs: Preparing Your Apps for iOS 27]]></title><description><![CDATA[From world-class developers, straight to you.]]></description><link>https://thosewhoswift.substack.com/p/devs-for-devs-preparing-your-apps</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/devs-for-devs-preparing-your-apps</guid><pubDate>Sun, 09 Aug 2026 20:30:33 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey, the <em><strong>Those Who Swift</strong></em> team is here! &#128075;</p><p>Not on Wednesday as usual, from now on we&#8217;ll see each other twice a week. Welcome to our new newsletter segment, &#8220;<em><strong>Devs for Devs</strong></em>&#8221;</p><p>We used to run a whole separate newsletter for this called &#8220;<em><strong>Indie App Devs</strong></em>&#8221; Now, every Sunday, we&#8217;ll feature a new segment from world-class, well-known devs here.</p><p>We&#8217;re starting with Natalia Panferova. I&#8217;m sure she doesn&#8217;t need to be introduced to you. She&#8217;s a software engineer, author, and former member of Apple&#8217;s core SwiftUI team, where she helped design and build SwiftUI APIs.</p><p>She has worked with startups and large companies across Europe and New Zealand, building web and mobile applications.</p><p>And without further ado, let&#8217;s jump into today&#8217;s topic and prepare our apps for iOS 27.</p><p>P.S. You can check out Natalia&#8217;s work on <strong><a href="https://www.linkedin.com/in/nataliapanferova/">LinkedIn</a></strong>, <strong><a href="https://x.com/natpanferova">X</a></strong>, and her <strong><a href="https://nilcoalescing.com">website</a></strong>.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6UZe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!6UZe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!6UZe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!6UZe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6UZe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:347072,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210277491?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6UZe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!6UZe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!6UZe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!6UZe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e39e294-848d-434f-aeea-9a9b075cc15e_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><p>Since WWDC, I&#8217;ve been spending a lot of time exploring the changes coming in iOS 27 and working through what they mean for the apps I maintain, from rebuilding them with Xcode 27 and testing layouts at different sizes to reviewing SwiftUI changes that may affect existing code.</p><p>There are plenty of new APIs to explore, and it&#8217;s always tempting to start adding them straight away. But for an existing app, I think it&#8217;s more useful to begin by checking how well it works with the new SDK and system behavior. Much of this preparation isn&#8217;t about adopting new APIs at all. It&#8217;s about making sure existing layouts work when the app is resized, reviewing the visual changes that come from rebuilding with Xcode 27, and addressing new compiler errors and warnings in existing SwiftUI code.</p><p>The amount of work will be different for every app, but these are the main areas I&#8217;d recommend reviewing.</p><h2>Supporting Resizable iPhone Apps</h2><p>One of the most significant changes in iOS 27 is that iPhone apps can now appear at more flexible sizes. This makes it increasingly difficult to assume that an iPhone app will always run in a narrow portrait window or at one of a small set of familiar screen dimensions.</p><p>Until recently, our walking app Strolly only supported portrait orientation on iPhone. It makes heavy use of non-modal sheets placed over a map, allowing users to adjust their route preferences while continuing to interact with the content underneath. When we added landscape support in preparation for iOS 27, we found that the system sheet didn&#8217;t preserve its non-modal presentation after rotation. It became a full-screen sheet and covered most of the map.</p><p>We ended up building an adaptive panel that appears along the bottom in a narrow layout and moves to the trailing edge when enough width becomes available. The layout decision is based on the actual scene width rather than the device type or horizontal size class. This lets the same presentation adapt to iPhone rotation, iPad multitasking, and window resizing.</p><p>Not every app will need a custom presentation, but it&#8217;s worth checking for similar assumptions throughout the interface. Fixed frames, hard-coded screen dimensions, and layouts that switch only on device idiom can all become problematic when the available space changes.</p><p>Xcode 27 includes resize handles in SwiftUI previews and a resizable iOS simulator, which make it easier to test a range of widths without relying only on predefined devices. I&#8217;d pay particular attention to toolbars, sheets, overlays, forms, keyboards, and any screen that places several controls side by side.</p><p>It&#8217;s also important to check that state remains stable while the available size changes. Controls shouldn&#8217;t disappear or become difficult to reach, and resizing shouldn&#8217;t unexpectedly reset navigation, scroll position, selections, or data owned by the view hierarchy.</p><h2>Reviewing the Liquid Glass Appearance</h2><p>Apps built with Xcode 27 automatically receive the latest refinements to the Liquid Glass design. Many of these changes come through standard SwiftUI and UIKit components, so rebuilding the app and inspecting the result should come before adding custom glass effects.</p><p>Navigation bars, tab bars, sheets, menus, toolbars, and standard controls may all look slightly different. Custom interfaces can also produce unexpected results when they place translucent controls over photographs, maps, gradients, or similarly colored content.</p><p>I&#8217;d test these screens with accessibility settings such as Increased Contrast and Reduce Transparency. Users have more control over the appearance of Liquid Glass in iOS 27, so the interface should remain clear across those variations rather than depending on one specific level of transparency.</p><p>Toolbars need some extra attention now that apps can appear at more sizes. Xcode 27 adds SwiftUI APIs for controlling which toolbar items remain visible and which move into the overflow menu. We can assign a visibility priority to important groups, deliberately place less frequently used actions in a <code>ToolbarOverflowMenu</code>, and use the <code>topBarPinnedTrailing</code> placement for an action that should always remain available.</p><p>Before adopting these APIs, I&#8217;d resize the existing toolbar and see what the system does by default. Standard behavior may already be sufficient. Where it&#8217;s not, the new controls let us express which actions are most important without replacing the system toolbar with a custom implementation.</p><h2>Building the Existing App with Xcode 27</h2><p>I like to begin a new SDK migration by building the existing project before introducing any new features. This keeps compiler errors and warnings caused by the new SDK separate from issues introduced while adopting new APIs.</p><p>Building with Xcode 27 brings a few SwiftUI changes worth checking. One of the most notable is that <code>@State</code> is now a macro rather than a property wrapper. This fixes a long-standing performance issue when an <code>@Observable</code> class is used as the default value of a state property. The initial expression is now evaluated lazily, so the observable model is created only when SwiftUI sets up the state storage rather than every time the view value is recreated.</p><p>We should still avoid assigning an observable model to <code>@State</code> inside a view initializer. This creates a new model whenever the initializer runs, even though SwiftUI preserves only the first value in its state storage. If the model depends on changing input from the parent, it can also retain stale data because later assignments are ignored.</p><p>Custom environment values are another area to check. Xcode 27 now warns when an <code>@Entry</code> declaration creates a new class instance directly in its default expression. Every fallback environment lookup would otherwise return a different reference, which can make SwiftUI treat the value as changed and reevaluate dependent views unnecessarily.</p><p>Some existing view expressions can produce new ambiguity errors because SwiftUI&#8217;s result builders are now unified under <code>ContentBuilder</code>. For example, expressions passed directly to overloaded APIs such as <code>overlay</code> and <code>background</code> may need to use their trailing-closure variants so the compiler can select the intended overload.</p><h2>Adopting New SwiftUI APIs</h2><p>Once the app builds and works correctly, we can start looking for places where the new APIs solve an existing problem or let us remove custom code.</p><p>Swipe actions are no longer limited to rows inside a <code>List</code>. In iOS 27, applying <code>swipeActionsContainer()</code> to a <code>ScrollView</code> enables coordinated swipe actions for rows in a <code>LazyVStack</code>, <code>LazyVGrid</code>, or custom layout.</p><p>SwiftUI also has new native reordering APIs for stacks, grids, and other containers. We apply <code>reorderable()</code> to the dynamic content and <code>reorderContainer(for:)</code> to the enclosing container. SwiftUI manages the drag preview, insertion placeholder, and animation, while the app receives a <code>ReorderDifference</code> to apply to its model.</p><p><code>AsyncImage</code> has gained new initializers that accept a <code>URLRequest</code>, giving us control over request headers, cache policy, and timeout without requiring a separate image-loading implementation. A new <code>asyncImageURLSession()</code> modifier can provide a custom session to asynchronous images in part of the view hierarchy.</p><p>These are all useful improvements, but an app doesn&#8217;t need to adopt them simply because they are new. I&#8217;d start with areas where the current implementation is limiting the interface, requires significant custom coordination, or has been difficult to maintain.</p><p>If the app continues supporting older versions of iOS, new APIs will also need an availability strategy. In some cases, the new behavior can be an enhancement on iOS 27 while the existing implementation remains as the fallback. In others, it may be better to wait until the deployment target can move forward rather than maintain two complicated code paths.</p><h2>Testing Before the Public Release</h2><p>An app prepared for iOS 27 still needs to work correctly on every version it supports. I&#8217;d test the main flows on both iOS 27 and the oldest supported system, paying particular attention to code guarded by availability checks.</p><p>Accessibility and localization can expose layout issues that don&#8217;t appear with default settings. I&#8217;d check the main screens with large Dynamic Type sizes, VoiceOver, Increased Contrast, Reduce Motion, and longer localized strings. These tests become especially useful alongside resizing because the most difficult layouts often combine a narrow width with larger content.</p><p>For performance-sensitive apps, Instruments 27 adds Run Comparisons for measuring changes between profiling sessions and expands the Swift executors instrument for investigating work running on the main actor and other executors. iOS 27 also introduces a new Swift-first MetricKit API. It continues to deliver app-health metrics and crash and hang diagnostics, while adding memory exception diagnostics and the ability to break metrics down by app-defined states.</p><p>Finally, I&#8217;d avoid leaving all of this work until the last beta. APIs and behavior can still change throughout the beta period, but testing early gives us more time to report issues through Feedback Assistant and adjust the app without rushing just before the public release.</p><div><hr></div><h2>SwiftUI Books with 20% off! &#128218;</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XKb2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XKb2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png 424w, https://substackcdn.com/image/fetch/$s_!XKb2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png 848w, https://substackcdn.com/image/fetch/$s_!XKb2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png 1272w, https://substackcdn.com/image/fetch/$s_!XKb2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XKb2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png" width="1456" height="436" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b828e329-4ff7-4ed2-a778-973274c35774_2538x760.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:436,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:283978,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/210277491?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XKb2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png 424w, https://substackcdn.com/image/fetch/$s_!XKb2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png 848w, https://substackcdn.com/image/fetch/$s_!XKb2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png 1272w, https://substackcdn.com/image/fetch/$s_!XKb2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb828e329-4ff7-4ed2-a778-973274c35774_2538x760.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>New SDK releases are exciting, but they also remind me how important it is to understand the behavior behind the APIs we use. Many of the issues Xcode 27 now helps us identify are related to the same fundamental topics we deal with every year: view identity, state ownership, data dependencies, layout, and performance.</p><p>I cover these foundations in <a href="https://books.nilcoalescing.com/swiftui-fundamentals">SwiftUI Fundamentals</a>, which explains SwiftUI&#8217;s core principles and underlying mechanisms in depth. My newer book, <a href="https://books.nilcoalescing.com/the-swiftui-way">The SwiftUI Way</a>, builds on that foundation with practical guidance for structuring production apps, managing data flow, minimizing unnecessary view updates, and avoiding subtle anti-patterns as projects grow.</p><p>Both books include free lifetime updates. I recently released a substantial update to &#8220;The SwiftUI Way&#8221; with expanded guidance on data flow, view performance, stable custom bindings, environment values, and the changes to <code>@State</code> in Xcode 27.</p><p>Readers of this newsletter can use the coupon code <strong>ThoseWhoSwift"</strong> to get <strong>20% off</strong> any of my books or book bundles until the end of August. You can find all the available options on the <strong><a href="https://books.nilcoalescing.com/">Nil Coalescing books website</a>.</strong></p>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 278]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-278</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-278</guid><pubDate>Wed, 05 Aug 2026 20:00:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>iOS 27 shipped, and now the real work starts &#8212; not adopting the shiny new features, but finding where they rub against the app you&#8217;ve already built. Liquid Glass is the most visible example: stunning in the keynote, strange in a UIKit view that predates it by a decade. But it&#8217;s the same story across the cycle &#8212; the gap between what demos cleanly and what survives contact with a real, aging codebase. This week is about that gap, and the field notes people are writing as they hit it.</p><p>Hit an iOS 27 surprise in your own app? Reply and tell me &#8212; I&#8217;m collecting the good war stories.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xv4-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:454072,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Xv4-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h1>When the Glass Meets the Girder</h1><p><em>with Divya Ravi &#8212; Technical Editor, iOS engineer</em></p><p>There&#8217;s a particular moment in every major design transition that nobody puts in the keynote. It&#8217;s the moment you build your project against the new SDK, launch the app you&#8217;ve shipped for years, and watch something you didn&#8217;t touch look subtly, unmistakably wrong. A button with the wrong corner. A bar that doesn&#8217;t blur where it used to. A control that was crisp yesterday and is now floating in a way you never asked for. Welcome to Liquid Glass in production.</p><p>I want to talk about that moment honestly, because the discourse around a new design language tends to split into two unhelpful camps. One says it&#8217;s gorgeous and you should adopt it everywhere immediately. The other says it&#8217;s a regression and you should fight it. Both miss the actual engineering reality, which is more interesting and more useful: a system-wide design language is not a coat of paint you apply. It&#8217;s a set of assumptions the framework now makes about your views &#8212; and the friction you feel is the distance between those new assumptions and the ones your codebase was built on.</p><p>Here&#8217;s the thing about Liquid Glass specifically. It&#8217;s a material, and materials are contextual by nature. A glass button assumes it sits on top of something worth seeing through. A minimizing bar assumes there&#8217;s content behind it that benefits from the effect. These are reasonable assumptions &#8212; until they meet a ten-year-old UIKit view hierarchy that was architected when every surface was opaque and every layer was flat. The framework does its best to adapt your old views to the new material, and most of the time it succeeds. The pitfalls live in the cases where it can&#8217;t, and those cases are exactly the ones a keynote demo will never show you, because keynote demos are built fresh, in the new world, with none of the accumulated assumptions of a real app.</p><p>This is why &#8220;just adopt it&#8221; is bad advice and &#8220;just fight it&#8221; is worse. The right posture is the one experienced engineers take toward any system-level change: understand what the framework now assumes, audit where your app violates those assumptions, and decide &#8212; deliberately, view by view &#8212; where to adapt and where to opt out. Liquid Glass gives you compatibility escape hatches for a reason. They&#8217;re not admissions of failure. They&#8217;re the framework acknowledging that your app has history, and history has weight.</p><p>The deeper lesson here isn&#8217;t about glass at all. It&#8217;s about what a design language actually is, underneath the marketing. It&#8217;s a contract about how surfaces relate to each other &#8212; depth, translucency, hierarchy, motion. When Apple changes that contract, every view you own is silently re-signed under the new terms, whether you read them or not. The apps that transition well aren&#8217;t the ones that adopt fastest or resist hardest. They&#8217;re the ones that treat the new contract as something to actually read &#8212; to understand where their existing UI agrees with it, where it conflicts, and where a five-minute opt-out saves a week of chasing a visual bug that was never a bug, just a disagreement about assumptions.</p><p>And Liquid Glass is only the most visible instance of a pattern that runs through this whole release. The same gap shows up everywhere real apps meet iOS 27: in the rendering model, in the architecture decisions that determine how easily you adapt, in the long-standing rough edges the framework quietly smoothed while you weren&#8217;t looking. The friction isn&#8217;t a bug in the release. It&#8217;s the tax every mature codebase pays when the platform moves &#8212; and the apps that pay it cheaply are the ones built to bend.</p><p>So if you&#8217;re staring at a control that looks wrong this week and you didn&#8217;t change it, you&#8217;re not losing your mind. The framework changed the terms. Go read them &#8212; the field guides are already being written, and the good ones map the pitfalls before you fall in. That&#8217;s the whole job this cycle: not adopting blindly, not resisting reflexively, but understanding precisely where the new platform meets the girder of everything you already built.</p><div class="pullquote"><p>What&#8217;s the strangest Liquid Glass behavior you&#8217;ve encountered in a real app? Hit reply&#8212;the best examples may appear in next week&#8217;s issue.</p></div><h1 style="text-align: center;">Related reading on this week's theme &#128218;</h1><p>iOS 27 meets real apps &#8212; the friction, the fixes, and the field notes. Liquid Glass leads, but the theme runs deeper: rendering, architecture, and the rough edges the framework quietly smoothed.</p><h3><a href="https://fatbobman.com/en/posts/liquid-glass-a-field-guide-to-uikit-compatibility-pitfalls/">Liquid Glass: A Field Guide to UIKit Compatibility Pitfalls</a></h3><p>Fatbobman documents the issues appearing when Liquid Glass meets existing UIKit code, including <code>UIBarButtonItem</code>, <code>UITabBarController</code>, <code>WKWebView</code>, and <code>UIStepper</code>, with practical workarounds for each.</p><p>A strong field guide for teams updating mature UIKit apps.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://livsycode.com/swiftui/changing-the-shape-of-glass-buttons-in-swiftui/">Changing the Shape of Glass Buttons in SwiftUI</a></h3><p>Livsy Code explores how Liquid Glass interacts with custom SwiftUI button shapes&#8212;and how to keep control of the corners when the defaults don&#8217;t fit your design.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.sagarunagar.com/blog/asyncimage-caching-wwdc-2026/">AsyncImage Finally Gets Caching in SwiftUI</a></h3><p>Sagar Unagar examines the new caching behavior in <code>AsyncImage</code> and what it means for network usage, repeated image loads, and scrolling performance.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://www.swiftbysundell.com/articles/swift-protocols-and-the-main-actor/">Swift Protocols and the Main Actor</a></h3><p>John Sundell on how actor isolation actually works with protocols &#8212; one of those foundational topics that quietly trips up even experienced developers as concurrency tightens.</p><p><strong><span>&#128205;Level: Advanced</span></strong></p><h3><a href="https://soumyamahunt.medium.com/stop-using-unchecked-sendable-2e00bd6cb122?source=rss-41d824adb39d------2">Stop Using @unchecked Sendable</a></h3><p>Soumya Mahunt makes the case against reaching for @unchecked Sendable as an escape hatch &#8212; what it really costs you, and the safer patterns that get you the same result.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://danielsaidi.com/blog/2026/06/08/introducing-listkit">Introducing ListKit: An Open-Source Library for SwiftUI Lists</a></h3><p>Daniel Saidi open-sources ListKit &#8212; reusable view components and extensions for the list problems everyone re-solves from scratch, from sectioning to selection to reordering.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187;</h1><h3><a href="https://www.nsvasilev.com/posts/approachable_concurrency/">Demystifying Thread Hopping with Swift 6.2 Approachable Concurrency</a></h3><p>Nikita Vasilev breaks down what actually happens when your code hops threads under Swift 6.2's approachable concurrency &#8212; and how to reason about it instead of guessing.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://www.pointfree.co/blog/posts/219-new-in-sqlitedata-sectioned-queries">New in SQLiteData: Sectioned Queries</a></h3><p>Point-Free adds sectioned queries to SQLiteData &#8212; a clean way to drive sectioned SwiftUI lists straight from your database without hand-rolling the grouping.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/news/?id=grx7lcto">Hello Developer: July 2026</a></h3><p>Apple's monthly developer digest rounds up the latest across the 27 platform releases &#8212; new documentation, sample code, and release notes, plus design resources and WWDC26 follow-ups worth a scan.</p><div><hr></div><h1 style="text-align: center;">Design &#127912;</h1><h3><a href="https://nilcoalescing.com/blog/KeepingCanvasInteractionsResponsiveWithFrameReprojection/">Keeping Canvas Interactions Responsive with Frame Reprojection</a></h3><p>Matthaus Woolard on maintaining responsive canvas interactions under the new rendering model using frame reprojection &#8212; a look under the hood of how modern SwiftUI stays smooth under load.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://livsycode.com/best-practices/the-ios-testing-strategy-agent-skill/">The iOS Testing Strategy Agent Skill</a></h3><p>Artem Mirzabekian shares an agent skill built around a practical iOS testing strategy.<br>It&#8217;s a useful example of turning team conventions&#8212;what to test, how to structure tests, and which edge cases matter&#8212;into repeatable instructions an AI coding agent can follow.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://rudrank.com/a-sols-work-shipping-with-gpt-5-6-sol">A Sol's Work: Shipping with GPT-5.6 Sol</a></h3><p>Rudrank Riyam gives a coding agent  a real engineering task, step away fro several hours, and the checks whether the final report matches what actually happened. <br>An honest account of where long-running agent work succeeds- and where human verification is still essential.</p><p><strong>&#128205;Level: Intermediate</strong></p><p></p><div><hr></div><h1 style="text-align: center;">Tutorials &#128210;</h1><h3><a href="https://toyboy2.medium.com/create-your-keyboard-for-app-theme-customizing-keyboard-in-swiftui-9c5283ed34e4">Building a Custom In-App Keyboard in SwiftUI</a></h3><p>Lee young-jun needed an in-app numeric keyboard and discovered SwiftUI's <code>TextField</code> gives you no clean hook for it &#8212; so he went spelunking through the UIKit view hierarchy to find the underlying <code>UITextField</code> and swap its <code>inputView</code>. A genuinely instructive walk through the messy reality of bridging SwiftUI and UIKit, dead ends included, ending with a sharp note on why banking and OTP apps ship custom keyboards in the first place.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://thorsten-stark.de/posts/2026-08-13-Shipping-A-SwiftData-App-With-iCloud-Sync-Part-3/">Shipping a SwiftData App with iCloud Sync</a></h3><p>Thorsten Stark walks through taking a SwiftData app from local persistence to multi-device CloudKit sync, including model constraints, container setup, and duplicate- record problems caused by concurrent device updates.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Video &#127909;</h1><h3><a href="https://www.youtube.com/watch?v=zfEt6PLICr8">SwiftUI Image Caching Explained: Faster Lists and Better Performance</a></h3><p>Stewart Lynch explains image caching in SwiftUI end to end &#8212; why uncached liimage loading hurts scrolling performance, how a caching layer helps, and how to build one clearly.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Those Who Swift - Issue 277]]></title><description><![CDATA[Your weekly dose of Swift!]]></description><link>https://thosewhoswift.substack.com/p/those-who-swift-issue-277</link><guid isPermaLink="false">https://thosewhoswift.substack.com/p/those-who-swift-issue-277</guid><pubDate>Wed, 29 Jul 2026 20:01:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FQA8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed0e7ea-c533-4c89-ac9b-f1bc72c616e2_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 style="text-align: center;">Weekly note &#9999;&#65039;</h2><p>Last week the iOS 27 beta landed and everyone braced for what breaks. This week is the other side of that coin &#8212; the quiet, genuinely useful APIs Apple slipped into iOS 27 that make code you already write simpler. The stuff that never gets a keynote slide, but shows up in your codebase for years.</p><p>Got a favorite iOS 27 API nobody&#8217;s talking about? Hit reply &#8212; I&#8217;m collecting them.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xv4-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png" width="1456" height="364" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:364,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:454072,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Xv4-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 424w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 848w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 1272w, https://substackcdn.com/image/fetch/$s_!Xv4-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbafe630a-0cf7-4488-acd7-aca4fe01a151_1584x396.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></div></figure></div><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OSFg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png 424w, https://substackcdn.com/image/fetch/$s_!OSFg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png 848w, https://substackcdn.com/image/fetch/$s_!OSFg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png 1272w, https://substackcdn.com/image/fetch/$s_!OSFg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OSFg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png" width="1200" height="50" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:50,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4106,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/208699686?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OSFg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png 424w, https://substackcdn.com/image/fetch/$s_!OSFg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png 848w, https://substackcdn.com/image/fetch/$s_!OSFg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png 1272w, https://substackcdn.com/image/fetch/$s_!OSFg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90e69de-2f4e-482f-babd-c9de8ec2f9e5_1200x50.png 1456w" sizes="100vw"></picture><div></div></div></div></figure></div><h3><a href="https://www.revenuecat.com/release/meet-rico-your-embedded-app-growth-advisor-2026-05-05/?utm_medium=sponsored&amp;utm_source=iOSDevTools&amp;utm_campaign=newsletter&amp;utm_content=rico">The AI Agent for Your RevenueCat Data</a></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6k4F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6k4F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png 424w, https://substackcdn.com/image/fetch/$s_!6k4F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png 848w, https://substackcdn.com/image/fetch/$s_!6k4F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png 1272w, https://substackcdn.com/image/fetch/$s_!6k4F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6k4F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png" width="554" height="377.8517298187809" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:828,&quot;width&quot;:1214,&quot;resizeWidth&quot;:554,&quot;bytes&quot;:388675,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/208699686?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6k4F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png 424w, https://substackcdn.com/image/fetch/$s_!6k4F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png 848w, https://substackcdn.com/image/fetch/$s_!6k4F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png 1272w, https://substackcdn.com/image/fetch/$s_!6k4F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bbff3a9-c205-485d-b274-ede1253d7b9c_1214x828.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Meet Rico! &#128075;</h4><p>RevenueCat&#8217;s embedded AI app growth advisor for subscription apps. Ask questions about churn, trials, pricing, paywalls, or retention and get answers using your app&#8217;s real subscription data directly inside RevenueCat and Slack.</p><p>Built to help iOS developers make smarter monetization decisions faster.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.revenuecat.com/release/meet-rico-your-embedded-app-growth-advisor-2026-05-05/?utm_medium=sponsored&amp;utm_source=iOSDevTools&amp;utm_campaign=newsletter&amp;utm_content=rico&quot;,&quot;text&quot;:&quot;Try Rico today!&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://www.revenuecat.com/release/meet-rico-your-embedded-app-growth-advisor-2026-05-05/?utm_medium=sponsored&amp;utm_source=iOSDevTools&amp;utm_campaign=newsletter&amp;utm_content=rico"><span>Try Rico today!</span></a></p><div><hr></div><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png" width="1456" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:131570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://thosewhoswift.substack.com/i/205959331?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kSUd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 424w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 848w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1272w, https://substackcdn.com/image/fetch/$s_!kSUd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff19657fb-691d-412f-b0f2-e1656dc6fb73_1584x300.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><h1>The Quiet Half of iOS 27</h1><p><em>with Divya Ravi &#8212; Technical Editor, iOS engineer, creator of FoundationModelsKit</em></p><p>Every WWDC has two halves. There&#8217;s the loud half &#8212; the keynote feature, the framework everyone live-tweets, the thing that lands on every &#8220;what&#8217;s new in iOS 27&#8221; listicle by Tuesday afternoon. And then there&#8217;s the quiet half: the small API refinements that never trend, never get a session to themselves, and turn out to matter far more to your day-to-day code than the headline act ever will.</p><p>I want to make the case for the quiet half &#8212; because this year it&#8217;s unusually good, and because the quiet changes are the ones that actually change how you write things.</p><p>Here&#8217;s the pattern I keep noticing in iOS 27. For years, a whole category of ordinary UI work lived in an awkward middle ground: not hard exactly, but not supported either. Swipe actions only worked inside List. Reordering only worked inside List. Adaptive panels meant hand-rolling a detent system and hoping it behaved in landscape. Toolbar customization meant quietly fighting the framework&#8217;s defaults. You could do all of it &#8212; you just did it by working around the framework instead of with it. Every iOS codebase of a certain age has a folder full of these workarounds, and everyone who&#8217;s touched one knows the specific dread of opening it.</p><p>What iOS 27 quietly does is close a lot of those gaps at once. Swipe actions work outside List now. Reordering has real APIs instead of a custom drop-delegate dance. Size classes gave way to a more honest model of available space &#8212; which suddenly matters, now that iPhone apps resize. None of these got a dramatic reveal. All of them delete code you currently maintain.</p><p>And that&#8217;s the real point, the one I&#8217;d underline twice. The loud features <em>add capability</em> &#8212; new things your app can do. The quiet features <em>remove liability</em> &#8212; workarounds you no longer have to own. Those two are not equally valuable, and we consistently act like they are. A new capability is something you adopt when you have the time and the reason. A removed workaround is tech debt that stops being your problem the moment you migrate &#8212; no adoption required, no feature to design, just deletion. And deletion compounds. Every custom reordering implementation you delete is code you no longer test, no longer debug, no longer explain to the engineer who inherits that file in eighteen months.</p><p>There&#8217;s a reason this half gets ignored, and it isn&#8217;t laziness. It&#8217;s that the quiet changes don&#8217;t announce themselves. Nobody writes a breathless thread titled &#8220;swipe actions now work in a ScrollView.&#8221; It doesn&#8217;t demo well. It doesn&#8217;t screenshot. But six months from now, the teams that went looking for these changes will have measurably simpler codebases than the teams that only adopted the headline features &#8212; not because they&#8217;re better engineers, but because they read past the first screen of the release notes.</p><p>So here&#8217;s my actual advice for this cycle, and it&#8217;s almost aggressively practical: before you build anything custom in iOS 27, check whether Apple already built it. The odds this year are genuinely good that they did. The framework spent this release catching up to a pile of things we&#8217;ve been hand-rolling for half a decade, and the catch-up is sitting in the quiet half, waiting for someone to notice.</p><p>Read the release notes to the end. That&#8217;s the whole tip. The loud half will find you on its own &#8212; it always does. The quiet half you have to go looking for, and this year, it more than pays for the trip.</p><div><hr></div><h1>Related reading on this week's theme &#128218;</h1><h3><a href="https://nilcoalescing.com/blog/BuildingAdaptiveNonModalPanelsInSwiftUI/">Building Adaptive Non-Modal Panels in SwiftUI</a></h3><p>Matthaus Woolard builds a detent-based, non-modal sheet that adapts from a bottom panel in portrait to an edge-aligned one in landscape &#8212; exactly the kind of thing you used to hand-roll and pray over.</p><p><strong>&#128205;Level: Advanced</strong></p><h3><a href="https://swiftwithmajid.com/2026/06/23/taking-control-of-toolbar-items-in-swiftui/">Taking Control of Toolbar Items in SwiftUI</a></h3><p>Majid Jabrayilov on iOS 27's new toolbar APIs &#8212; finer control over appearance now that the design language splits content and controls into separate layers.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://danielsaidi.com/blog/2026/05/22/making-a-swiftui-sheet-automatically-size-to-fit-its-content">Making a SwiftUI Sheet Automatically Size to Fit Its Content</a></h3><p>Daniel Saidi on getting sheets to size to their content instead of defaulting to half the screen &#8212; a small, satisfying win over a default everyone has fought at some point.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://antongubarenko.substack.com/p/ios-27-uibarminimization">iOS 27: UIBarMinimization</a></h3><p>Anton Gubarenko on iOS 27&#8217;s new bar-minimization behavior &#8212; how to control the way bars collapse and expand, and where it fits in a modern multi-window layout.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://serialcoder.dev/text-tutorials/swiftui/wwdc26-reordering-items-in-swiftui-lists-and-grids/">WWDC26: Reordering Items in SwiftUI Lists and Grids</a></h3><p>Gabriel Theodoropoulos walks through the new reorderable and reorderContainer APIs &#8212; native drag-to-reorder that finally works beyond List.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://fatbobman.com/en/posts/from-size-class-to-available-space/">From Size Class to Available Space</a></h3><p>Fatbobman on why iOS 27 moves past size classes toward a more honest model of available space &#8212; and why that matters now that iPhone apps resize.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">This Week in Those Who Swift &#128478;&#65039;</h1><p style="text-align: center;"><em>The latest across Swift, Apple, AI, and the community.</em></p><h1 style="text-align: center;">Swift Around the Web &#127760;</h1><h3><a href="https://tanaschita.com/ios-code-signing-provisioning/">Understanding Code Signing and Provisioning in iOS</a></h3><p>Natascha Fadeeva demystifies the whole signing chain &#8212; certificates, provisioning profiles, entitlements &#8212; as a practical mental model instead of a black box you fight at build time.</p><p><strong><span>&#128205;Level: Intermediate</span></strong></p><h3><a href="https://tanaschita.com/swiftdata-persistence-boundaries/">Keeping SwiftData Behind a Boundary</a></h3><p>Natascha Fadeeva on wrapping SwiftData behind a protocol boundary so your app isn't married to the framework &#8212; cleaner tests, easier migration, fewer surprises down the line.</p><p><strong>&#128205;Level: Intermediate</strong></p><h3><a href="https://swiftwithmajid.com/2026/06/16/swipe-actions-outside-of-list-in-swiftui/">Swipe Actions Outside of List in SwiftUI</a></h3><p>Majid Jabrayilov on one of iOS 27&#8217;s most overdue additions: swipeActions now works in a ScrollView and custom layouts, not just List.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Coding &#128104;&#8205;&#128187;</h1><h3><a href="https://www.avanderlee.com/xcode/ios-time-zone-testing-physical-iphone/">Test iOS Apps in Different Time Zones</a></h3><p>Antoine van der Lee on catching an entire class of date bugs before your users do &#8212; how to actually run your app under a different time zone on a physical device.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">Apple &#127823;</h1><h3><a href="https://developer.apple.com/news/?id=awedznci">New Business Terms for Link-Out Purchases</a></h3><p>Apps that communicate or promote offers for digital goods outside the App Store will be subject to new terms &#8212; an initial acquisition fee, a store services fee, and, for the EU External Purchase Link Entitlement, the Core Technology Commission.</p><h3><a href="https://developer.apple.com/help/app-store-connect/reference/app-uploads/on-demand-resources-size-limits/">On-Demand Resources Are Deprecated in iOS 27</a></h3><p>Apple's App Store Connect docs now list On-Demand Resources (and the NSBundleResourceRequest API) as deprecated across iOS 27, iPadOS 27, tvOS 27, and watchOS 27, with support to be removed in a future release. Apple recommends migrating to Background Assets.</p><div><hr></div><h1 style="text-align: center;">Design &#127912;</h1><h3><a href="https://nilcoalescing.com/blog/BlendModesInSwiftUI/">Blend Modes in SwiftUI</a></h3><p>Natalia Panferova walks every SwiftUI blend mode through focused visuals and real use cases &#8212; and shows how to control exactly which views participate in each operation.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><h1 style="text-align: center;">AI &amp; Tooling &#129302;</h1><h3><a href="https://www.avanderlee.com/debugging/introducing-diagnostics-improved-debugging-and-user-support/">User Diagnostics Reports: Solving Bugs Faster with AI Agents</a></h3><p>Antoine van der Lee on turning real user diagnostic reports into fixes &#8212; feeding crash context to an AI agent and getting back a reviewed pull request instead of a raw stack trace.</p><p><strong>&#128205;Level: Intermediate</strong></p><p></p><div><hr></div><h1 style="text-align: center;">Tutorials &#128210;</h1><h3><a href="https://rudrank.com/exploring-foundation-models-bridging-gemini-video-with-customsegment">Bridging Gemini Video with Foundation Models and CustomSegment</a></h3><p>Rudrank Riyam routes video through a LanguageModelSession to Gemini &#8212; Apple's on-device model takes images but not video, so he bridges the gap with a custom Transcript.CustomSegment and a LanguageModelExecutor, verifying each layer along the way.</p><p><strong>&#128205;Level: Advanced</strong></p><div><hr></div><h1 style="text-align: center;">Video &#127909;</h1><h3><a href="https://www.youtube.com/watch?v=N_0P9mp5T6w">iOS 27 SwiftUI Updates in Action</a></h3><p>A hands-on tour of this cycle's SwiftUI changes in a real project &#8212; reorderable views, swipe actions outside List, and the new navigation and presentation APIs, shown in code.</p><p><strong>&#128205;Level: Intermediate</strong></p><div><hr></div><p style="text-align: center;">Thanks for reading Those Who Swift!</p><p style="text-align: center;">Subscribe for free to receive new posts. &#128071;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://thosewhoswift.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://thosewhoswift.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item></channel></rss>