<?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[Jacob’s Tech Tavern]]></title><description><![CDATA[The #1 iOS newsletter on Substack. Join 10,000 engineers learning advanced concurrency, agentic iOS engineering, and Swift performance every week.]]></description><link>https://blog.jacobstechtavern.com</link><image><url>https://substackcdn.com/image/fetch/$s_!LJp-!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png</url><title>Jacob’s Tech Tavern</title><link>https://blog.jacobstechtavern.com</link></image><generator>Substack</generator><lastBuildDate>Sat, 07 Nov 2026 15:54:25 GMT</lastBuildDate><atom:link href="https://blog.jacobstechtavern.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Jacob Bartlett]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[jacobbartlett@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[jacobbartlett@substack.com]]></itunes:email><itunes:name><![CDATA[Jacob Bartlett]]></itunes:name></itunes:owner><itunes:author><![CDATA[Jacob Bartlett]]></itunes:author><googleplay:owner><![CDATA[jacobbartlett@substack.com]]></googleplay:owner><googleplay:email><![CDATA[jacobbartlett@substack.com]]></googleplay:email><googleplay:author><![CDATA[Jacob Bartlett]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Oh, sh*t, I forgot about Grand Central Dispatch]]></title><description><![CDATA[A comprehensive toolbox to grasp queues and concurrency]]></description><link>https://blog.jacobstechtavern.com/p/grand-central-dispatch</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/grand-central-dispatch</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 04 Nov 2026 16:01:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!lCfs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lCfs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lCfs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!lCfs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!lCfs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!lCfs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lCfs!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:930044,&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://blog.jacobstechtavern.com/i/222373310?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lCfs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!lCfs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!lCfs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!lCfs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f76dd20-bf1a-469b-ace2-302ac8984294_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>GCD, or Grand Central Dispatch, is one of the underlying frameworks of iOS development. It is to Swift Concurrency what UIKit is to SwiftUI: the crufty older sibling, underpinning decades of working legacy code, who still gets the job done.</p><p>You won&#8217;t encounter it so often on a brand-new project, but you&#8217;ll see it in many situations:</p><ul><li><p>Any codebase that maintains working legacy code from before 2021 will have heaps of GCD.</p></li><li><p>When you are playing with fundamental frameworks like AVFoundation.</p></li><li><p>A lot of job interview tech tests will use GCD in some way.</p></li></ul><p>I had this exact situation last year as part of my <a href="https://blog.jacobstechtavern.com/p/interview-grind">recent job grind</a>: a final-stage &#8220;Concurrency&#8221; technical test interview where I was told to swot up on Grand Central Dispatch in advance.</p><p>This heads-up was a lifesaver, allowing me to drill some classic concurrency problems, but in the interview I ended up spending most of my energy trying to demonstrate I <em>knew the toolkit</em> rather than <em>solving the actual problem</em>. Whoops.</p><p>I thought about calling this article &#8220;Practical Applications of Grand Central Dispatch in Modern Swift Code&#8221;, but I wanted to respect your intelligence and not bullsh*t you. This is a (heavily) spruced-up form of my revision notes. Perhaps it will help you the next time you need to grind for a concurrency test. </p><p><em>&#8230;and even more hopefully, the other iOS bloggers will stop bullying me for never having written a piece on GCD.</em></p><div><hr></div><h5>Sponsored Link</h5><blockquote><h4><strong><a href="http://www.runway.team/webinar/the-new-mobile-bottleneck-shipping-ai-written-code?utm_source=jacobs-tech-tavern&amp;utm_medium=newsletter&amp;utm_campaign=bottleneck-webinar&amp;utm_content=2026-11-04">Can your mobile release handle AI-generated code?</a></strong></h4><p>AI multiplied the amount of code your team writes. The process that ships it hasn&#8217;t changed: same release trains, same quality gates, same manual handoffs. Join a live panel with mobile engineering leaders from Thomson Reuters, Mercado Libre and Salesforce to discuss how that volume changes the release process. </p><p>Nov 12, 10am PT / 1pm ET. Sign up <a href="http://www.runway.team/webinar/the-new-mobile-bottleneck-shipping-ai-written-code?utm_source=jacobs-tech-tavern&amp;utm_medium=newsletter&amp;utm_campaign=bottleneck-webinar&amp;utm_content=2026-11-04">here</a>.</p></blockquote><div><hr></div><h2>Contents</h2><ul><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;the-history-of-gcd-why-it-was-important">The History of GCD (why it was important)</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;how-libdispatch-works-under-the-hood">How libdispatch works under the hood</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;gcd-basics">GCD Basics</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;queues">Queues</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;dispatchqueuesync-and-deadlocks">DispatchQueue.sync and deadlocks</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;quality-of-service">Quality of service</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;asyncafter">asyncAfter</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;parallelism-and-dispatchgroup">Parallelism and DispatchGroup</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;locks">Locks</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;thread-safe-reads-and-writes">Thread-safe reads and writes</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;target-queues">Target queues</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;advanced-gcd">Advanced GCD</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;dispatchworkitem-and-cancellation">DispatchWorkItem and cancellation</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;flags-and-barriers">Flags and Barriers</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;semaphores">Semaphores</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;priority-inversion">Priority Inversion&#8482;</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/p/5d376703-18d4-4b20-94a5-356f908ce053#&#167;last-orders">Last Orders &#127866;</a></p></li></ul><div><hr></div><h2>The History of GCD (why it was important)</h2><p>With the 1996 acquisition of NeXT and Steve Jobs, the NeXTSTEP OS was merged into Mac to create Mac OS X. Objective-C became the dominant Apple platform language. But it took a long time for Apple to figure out a nice concurrency API. For a decade, Mac devs made do with semi-manual concurrency management using POSIX threads.</p><p>This API included <code>pthread_create()</code>, <code>pthread_join()</code>, <code>pthread_mutex_lock()</code>, and <code>pthread_cond_wait()</code>, for developers to manage individual threads and handle concurrency. These low-level threads are the fundamental abstraction underneath <code>NSThread</code>, GCD, and even Swift Concurrency.</p><p>But these APIs were a pain to use: developers would need a lot of fiddly boilerplate to perform basic tasks like downloading an image in the background, getting it back onto the main thread, and displaying it:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;c&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-c">static void *loadImageOnThread(void *context) {
    ImageLoader *loader = (__bridge_transfer ImageLoader *)context;
    // Slow, so off the main thread
    NSData *data = [NSData dataWithContentsOfURL:loader.url];
    // UI work back on main
    [loader performSelectorOnMainThread:@selector(showImageData:)
                             withObject:data
                          waitUntilDone:NO];
    return NULL;
}

- (void)loadImage {
    pthread_t thread;
    pthread_create(&amp;thread, NULL, loadImageOnThread, (__bridge_retained void *)self);
    pthread_detach(thread);
}

- (void)showImageData:(NSData *)data {
    self.imageView.image = [UIImage imageWithData:data];
}</code></pre></div><p>Most didn&#8217;t bother, leading to sluggish software across the board: a sea of spinning beach balls, as devs either locked up the main thread or failed to utilise their cores effectively.</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_!7fsm!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif 424w, https://substackcdn.com/image/fetch/$s_!7fsm!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif 848w, https://substackcdn.com/image/fetch/$s_!7fsm!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif 1272w, https://substackcdn.com/image/fetch/$s_!7fsm!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7fsm!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif" width="320" height="180" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:180,&quot;width&quot;:320,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The spinning beach ball of death&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 spinning beach ball of death" title="The spinning beach ball of death" srcset="https://substackcdn.com/image/fetch/$s_!7fsm!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif 424w, https://substackcdn.com/image/fetch/$s_!7fsm!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif 848w, https://substackcdn.com/image/fetch/$s_!7fsm!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif 1272w, https://substackcdn.com/image/fetch/$s_!7fsm!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bf8c2b6-4f0d-47f0-a607-958bf094daf6_320x180.gif 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>As well as having poor devex, pthreads were expensive to create, reserved a surprising amount of memory <em>(512kB, not that you asked)</em>, and had overhead any time you switched between them. POSIX gifted great power <em>and</em> great responsibility. Multithreading was a chaotic mess.</p><p>One of the surprising Hard Things&#8482; with manual thread management is <em>knowing how many threads to use</em>. Too few, and you leave resources on the table. Too many, and you incur tons of thread-creation-and-switching overhead.</p><p>But how do you know what&#8217;s going on in the system? You can&#8217;t!</p><p>What on Earth were we to do?</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_!0YzU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png 424w, https://substackcdn.com/image/fetch/$s_!0YzU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png 848w, https://substackcdn.com/image/fetch/$s_!0YzU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png 1272w, https://substackcdn.com/image/fetch/$s_!0YzU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!0YzU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png" width="300" height="169" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:169,&quot;width&quot;:300,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The Grand Central Dispatch logo&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 Grand Central Dispatch logo" title="The Grand Central Dispatch logo" srcset="https://substackcdn.com/image/fetch/$s_!0YzU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png 424w, https://substackcdn.com/image/fetch/$s_!0YzU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png 848w, https://substackcdn.com/image/fetch/$s_!0YzU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png 1272w, https://substackcdn.com/image/fetch/$s_!0YzU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee29690b-d799-4d3d-b3a9-bbc0c2b8e47a_300x169.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>Grand Central Dispatch, a.k.a. GCD, a.k.a.k.a. <em>libdispatch</em>, is a C library that lives deep in the OS. At its core, GCD maintains a &#8220;thread pool&#8221; of POSIX threads onto which it schedules work. These long-lived pthreads mitigate the overhead of constant thread creation and context-switching. Developers place work on dispatch queues, and let GCD handle the scheduling and thread management.</p><p>Now, the same image-loading task was as simple as this 6-liner:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;c&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-c">- (void)loadImage {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Slow, so off the main thread
        NSData *data = [NSData dataWithContentsOfURL:self.url];
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI work back on main
            self.imageView.image = [UIImage imageWithData:data];
        });
    });
}</code></pre></div><p>The rest of our examples will be pure Swift, but it&#8217;s useful to #KnowYourRoots.</p><p>GCD transferred thread management responsibility from developers to the OS, which works far better as the arbiter of <em>&#8220;what work should run on what thread when&#8221;</em>, since it has a full view of the system. The system knows whether other programs are competing for resources, or if you can go full throttle parallelising everything.</p><div><hr></div><h2>How libdispatch works under the hood</h2><p>In Foundation, pthreads are more commonly wrapped in <code>Thread</code> (formerly <code>NSThread</code>). Context-switching between these threads is expensive, because the kernel has to:</p><ol><li><p>Pause a thread running on a core</p></li><li><p>Store the state of its registers</p></li><li><p>Load up another thread onto a CPU core</p></li><li><p>Resume the work on the other thread</p></li></ol><p>GCD introduced the &#8220;queue&#8221; abstraction, a lightweight FIFO approach that abstracted away the pain of individual thread management and scheduling. Queues are extremely lightweight: just 128 bytes. You can spawn thousands of these with very little performance impact.</p><p>The key insight with queues is separating the logical entities of &#8216;threads&#8217; from &#8216;work to be done&#8217;. Threads tie closer to CPU cores, which physically perform the computation, whereas queues logically store what work will be scheduled next.</p><p>This abstraction makes concurrency straightforward for an engineer: just add work to a queue. The queue&#8217;s configuration determines whether it runs on the main thread or in the background, and how urgently it&#8217;s prioritised.</p><p>The system schedules work based on queues, dequeueing work and placing it on threads in the thread pool, rather than needing to create pthreads anew for each job.</p><p>On Darwin platforms, you can find <a href="https://github.com/apple-oss-distributions/libdispatch">libdispatch</a> on GitHub: part library, part kernel subsystem, and <em>all</em> beast.</p><p>Now let&#8217;s look at the toolkit available in GCD.</p><div><hr></div><h2>GCD Basics </h2><p>Understanding the fundamentals of GCD and queues is the bare minimum, even if you aren&#8217;t about to do an interview. You should be comfortable with stuff like:</p><ul><li><p>The basic dispatch queues on the system</p></li><li><p>How to create your own queues</p></li><li><p>Adding tasks to a queue</p></li><li><p>Managing priority</p></li><li><p>Avoiding deadlocks</p></li></ul><div><hr></div><h3>Queues</h3><p>There are really only two basic built-in queues to understand:</p><ul><li><p><code>DispatchQueue.main</code>, the main queue, which runs UI work</p></li><li><p><code>DispatchQueue.global()</code>, a background queue for any non-UI work</p></li></ul><p>You add async workloads to a queue by calling <code>async { }</code> on it with a closure.</p><p>If you take one thing away, make it this: how to perform work in the background, then bring the results back to the main thread to update your UI.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">func loadImage() {
    DispatchQueue.global().async {
        // Slow, so we run it off the main thread
        let data = try? Data(contentsOf: self.url)
        DispatchQueue.main.async {
            // UI work jumps back to main
            self.imageView.image = data.flatMap { UIImage(data: $0) }
        }
    }
}</code></pre></div><p>Your own queues might be serial (the default) or concurrent (by setting <code>attributes: .concurrent</code>). This determines whether jobs are dequeued and run one at a time, or whether multiple jobs can run on the queue simultaneously.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">let serial = DispatchQueue(label: "com.example.serial")
// Upload always runs after save 
serial.async { print("Save the draft") }
serial.async { print("Upload the draft") }

let concurrent = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)

// Ordering is non-deterministic 
concurrent.async { print("Resize photo 1") }
concurrent.async { print("Resize photo 2") }</code></pre></div><div><hr></div><h3>DispatchQueue.sync and deadlocks</h3><p>You can also call <code>sync</code> to introduce a <strong>blocking</strong> wait. This is helpful for protecting thread-safe access to critical sections of memory, or enforcing strict ordering, but you need to be careful not to deadlock.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">DispatchQueue.main.sync { }   // &#10060; guaranteed deadlock on main</code></pre></div><p>A classic concurrency interview puzzle is asking you&#8230; <em>&#8220;what happens when I run this code?&#8221;</em></p><p>You can pattern-match this by immediately saying <em>&#8220;it&#8217;ll deadlock&#8221;</em>, and look dead impressive. Why else would they ask you? But, if they&#8217;re cruel, you may have to step through it yourself:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">let queueA = DispatchQueue(label: "com.example.a")
let queueB = DispatchQueue(label: "com.example.b")

queueA.async {            // Runs on a background thread, holding queueA
    print("1") 
    queueB.sync {         // queueB is free, so this runs straight away
        print("2") 
        queueA.sync {     // Waits for queueA... which is busy running the outer block
            print("3")    // Never prints: A is waiting on B, and B is waiting on A
        }
        print("4")        // Never prints
    }
    print("5")            // Never prints
}
// Output: 1, 2, then deadlock</code></pre></div><p>We have a circular dependency: since we block <code>queueB</code>, and <code>queueA</code> is busy running <code>queueB.sync</code>, neither queue can make forward progress. The system deadlocks.</p><div><hr></div><h3>Quality of service</h3><p>When utilising a global queue, which runs in the background, we can tell the system how urgently to schedule the work using the QoS (quality of service) parameter.</p><ul><li><p><code>.userInteractive</code> is the highest urgency, with the system attempting to schedule it as soon as possible, since the user will be directly waiting for this</p></li><li><p><code>.userInitiated</code> work is pretty urgent, since for a good UX, you want to give a result to a user as soon as possible</p></li><li><p><code>.default</code> is the middle ground, which you get if you just call <code>DispatchQueue.global()</code> without a QoS</p></li><li><p><code>.utility</code> is less urgent, and might be delayed if the system is busy</p></li><li><p><code>.background</code> is for extremely non-time-sensitive workloads that the system will get round to when its resource usage is chill</p></li></ul><p>You can apply these QoS levels to your own queues, or even specific jobs.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">// You can set QoS on your own queues...
let imports = DispatchQueue(label: "com.example.imports", qos: .utility)

// Or set QoS it for a specific 
imports.async(qos: .userInitiated) {
    print("Quickly import the file the user just dropped")
}</code></pre></div><div><hr></div><h3>asyncAfter </h3><p>One of the most common queueing operations is delaying a piece of work. If you want a job to happen at some point in the future, set <code>asyncAfter(deadline:)</code>. The deadline is a <code>DispatchTime</code>, which wraps a <code>UInt64</code> storing the number of nanoseconds since you booted the device. Most of the time, you&#8217;ll write <code>.now() + X</code> where <code>X</code> is the number of seconds of delay you want. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    print("Hide the save confirmation")
}</code></pre></div><p>I&#8217;ve solved <em>many</em> UIKit animation bugs with the classic hacky <code>asyncAfter(deadline: .now() + 0.01)</code>. Usually that&#8217;s enough to ensure that the work lands on the main thread and jump-starts the visuals.</p><div class="callout-block" data-callout="true"><p>This article is exclusive to paid members. As well as this, you get:</p><p>&#9875;&#65039; Access my <a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 55+ paywalled articles</a> (including this one)<br>&#128640; Read <a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a> a month before anyone else<br>&#129525; Master concurrency with my <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a> and <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br>&#129489;&#8205;&#128640; Get a free copy of my 109-page eBook, &#8220;<a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a>&#8221;<br>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p></div>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/grand-central-dispatch">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[The new core skills: agentic engineering at Granola]]></title><description><![CDATA[My (winning!) submission for the Pragmatic Engineer essay contest]]></description><link>https://blog.jacobstechtavern.com/p/the-new-core-skills</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/the-new-core-skills</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 28 Oct 2026 16:03:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-z7i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>I originally entered this post in The Pragmatic Engineer&#8217;s &#8220;<a href="https://blog.pragmaticengineer.com/how-software-engineering-is-changing-an-essay-challenge/">How software engineering is changing</a>&#8221; essay contest, and was one of 7 prizewinners out of 125 entries! <br>Thanks a lot to <a href="https://x.com/GergelyOrosz">Gergely</a> for putting on the contest. I hope you enjoy.</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-z7i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-z7i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!-z7i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!-z7i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!-z7i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-z7i!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:1443006,&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://blog.jacobstechtavern.com/i/221697569?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-z7i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!-z7i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!-z7i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!-z7i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b2fb691-1299-465e-b1c4-87a94127fa41_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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><blockquote><p><em>In other news, I just released an eBook on agentic engineering.<br>Read to the end or click <a href="https://payhip.com/b/g2vIT">here</a> for more details.</em></p></blockquote><p>The last year has seen the fastest revolution in the history of software engineering. The abstraction level has rapidly shifted from writing code line-by-line, to babysitting agents as they write files, to creating whole systems using the latest frontier models.</p><p>Many engineering skills, once thought of as the most valuable, are in question. But other fundamentals are more crucial than ever.</p><p>Working at Granola, that AI notetaker unicorn, allows me to work close to the cutting edge as the art of building software evolves. Today we&#8217;re looking at the evergreen principles for working with agents, and discovering the new skills needed to be effective in this new age.</p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/december-2025-gpt-52-and-opus-45">December 2025: GPT-5.2 and Opus 4.5</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/january-2026-no-major-releases">January 2026: No major releases</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/february-2026-gpt-53-and-opus-46">February 2026: GPT-5.3 and Opus 4.6</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/march-2026-gpt-54">March 2026: GPT-5.4</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/april-2026-gpt-55-and-opus-47">April 2026: GPT-5.5 and Opus 4.7</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/may-2026-opus-48">May 2026: Opus 4.8</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/june-2026-fable-5-unleashed">June 2026: Fable 5 (unleashed)</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/july-2026-gpt-56-sol-opus-5-and-fable-5-nerfed">July 2026: GPT-5.6 Sol, Opus 5, and Fable 5 (nerfed)</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/august-2026-no-major-releases">August 2026: no major releases</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/september-2026-gpt-6-astra-fable-51-and-opus-55">September 2026: GPT-6 Astra, Fable 5.1, and Opus 5.5</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/october-2026-no-major-releases">October 2026: no major releases</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/221697569/last-orders">Last Orders &#127866;</a></p></li></ul><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>December 2025: GPT-5.2 and Opus 4.5</h2><p>I was burning out, a slack husband and a distracted dad.</p><p>After having, briefly, the time of my life as an <a href="https://blog.jacobstechtavern.com/p/my-wife-gave-me-100-days">indie writer</a>, I remembered I had a mortgage and began the dreaded tech job grind. I was early to the Claude Code research preview, and by now was fully AI-pilled, na&#239;vely forgetting the existence of hand-coding exercises. My most humiliating highlight was my xAI interview: for 45 minutes, the busiest iOS engineer on the planet stared in silence as I forgot trivial SwiftUI syntax like ForEach.</p><p>I want to tell you I was being strategic, moving to where the puck was going, seeking a frontier lab or top-tier application layer play, with geysers of capital and limitless token budgets, so I could hone the new skills needed to be an effective engineer. But I won&#8217;t let the narrative fallacy get in the way of a good reality: it was Q4, nobody was hiring, and I was spraying and praying.</p><p>Granola, the AI notetaker, got caught in the crossfire, and pulled me into their process late in the game. It was a Friday. I&#8217;d somehow managed to pack next week - the last before Christmas - with 3 final-stage on-site loops. After a very smooth system design round, Granola quickly followed up: they wanted me to do a take-home test. I was <em>this close</em> to leaving it until after Christmas, but that round <em>did</em> go pretty well. So be it.</p><p>I performed what, as far as I could tell, was state-of-the-art agentic engineering: an hour or two writing a <em>very</em> detailed prompt, defining architecture, file-by-file layout, and interfaces between each component. I let both Claude and Codex rip, picked my favourite result as a starting point, then spent a couple more hours single-threadedly massaging the output into a satisfying shape. It sparked joy. I submitted.</p><p>The best call I made was including my PROMPT.md in the submission. I guessed they&#8217;d appreciate it. They did. I was subsequently invited on-site to cap off the most stressful week of my life. I got the job.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gGwf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gGwf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png 424w, https://substackcdn.com/image/fetch/$s_!gGwf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png 848w, https://substackcdn.com/image/fetch/$s_!gGwf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png 1272w, https://substackcdn.com/image/fetch/$s_!gGwf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!gGwf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png" width="1456" height="917" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:917,&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_!gGwf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png 424w, https://substackcdn.com/image/fetch/$s_!gGwf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png 848w, https://substackcdn.com/image/fetch/$s_!gGwf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png 1272w, https://substackcdn.com/image/fetch/$s_!gGwf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06fe174-9ae6-41f2-b8c0-e25cffa781db_1804x1136.png 1456w" sizes="auto, 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><h2>January 2026: No major releases</h2><p>I spent most of January &#8220;unwinding&#8221;, having pushed my Granola start date back a few weeks to relax and replenish my energies. This was, of course, a total fabrication. I hustled to produce a months-thick blog backlog so I could focus 100% of my surplus energy on making an impression.</p><p>I also took the month to study the advice of my 2 divorced dads, Peter Steinberger and Boris Cherny, up-skilling myself on the modern cutting edge of agentic engineering so I could hit the ground running.</p><p><a href="http://x.com/steipete">Peter</a> is the fun weekend dad that financially supports me*. He taught me to rely on voice dictation to rapidly get my intent into an agent without painstakingly perfecting my prompts. His best advice, which has proved most timeless of all, was <em>&#8220;<a href="https://steipete.me/posts/just-talk-to-it">just talk to it</a>&#8221;</em>. Elaborate tooling, subagents, AGENTS.md, ultrathink, custom harnesses, and skills have all waxed and waned in relevance, while the models inexorably get better at understanding what you want.</p><p><em>*As a paid subscriber to my blog</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xx6h!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xx6h!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!xx6h!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!xx6h!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!xx6h!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!xx6h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg" width="530" height="299.09706546275396" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:886,&quot;resizeWidth&quot;:530,&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_!xx6h!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg 424w, https://substackcdn.com/image/fetch/$s_!xx6h!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg 848w, https://substackcdn.com/image/fetch/$s_!xx6h!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!xx6h!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a2a35ec-5377-4281-90b4-5d2fb70911f7_886x500.jpeg 1456w" sizes="auto, 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><a href="http://x.com/bcherny">Boris</a> is my Monday-to-Friday dad who pushes me, and just wants what&#8217;s best for me. He won&#8217;t reply to my Tweets, but will now-and-then offer nuggets of searing clarity, such as &#8220;<em>give Claude a way to verify its own work&#8221;</em>. This has become more critical over time: as the scope I can delegate to models grows, I can define more comprehensive verification to confidently ship at pace. More on that later.</p><p>Boris also inspired me to try <a href="https://x.com/bcherny/status/2007179832300581177">rolling multiple agents in parallel</a>. Frontier models, on higher effort levels, make fewer mistakes, but work slower. Agents compile, run smoke tests, read logs, and fix their work autonomously. These factors add up. <em>This</em> is why you go multi-agent. Not because you have severe ADHD (<a href="https://blog.jacobstechtavern.com/p/adhd-vs-alarmkit">which I do</a>), but because you can reliably delegate execution to a single agent for hours.</p><p>At the end of the month I began to fulfil my destiny as employee #50 of Granola, the vanguard of a massive (and ongoing) growth spurt. After several painful years hustling at <em><a href="https://blog.jacobstechtavern.com/p/the-side-hustle-from-hell">chat-to-us-when-you-have-traction </a></em><a href="https://blog.jacobstechtavern.com/p/the-side-hustle-from-hell">stage</a> startups, pre-seed, seed, and series As, I finally understand what people mean when they say there&#8217;s an electric difference in the air between a pre-PMF startup and post-PMF startup. There&#8217;s a million things to do, all of which we know will move the needle. The only limit is how fast we can ship.</p><p>As we&#8217;ll see, this turned out to be a good fit: both for the emerging agentic age, and my own personality.</p><div><hr></div><h2>February 2026: GPT-5.3 and Opus 4.6</h2><p>Classic Google famously gave its engineers <em>20% time</em>, the permission system to dream up new initiatives like Gmail and AdSense. Slightly less famously, this was actually <em>120% time</em>: engineers would tinker on top of their demanding workloads as they tried to <em>organise the world&#8217;s information</em> and <em>not be evil</em>.</p><p>Typically, I&#8217;ve gotten into minor trouble at work for attempting to have my cake and eat it; trying to make a name for myself via 20% projects, but stacking it <em>inclusively</em> rather than <em>on top of</em> my regular hours. The same line I heard throughout my education and into my career: <em>Focus. Stop getting distracted.</em></p><p>But this was in the beforetimes.</p><p>The Granola iOS app began life as a side project by Jonathan, one of our fullstack engineers, to find out if meetings-on-the-go had legs. It did! Tom, our mobile lead, was hired to take it to production. Like all good startups, Granola optimised for speed-of-validation to get it into the hands of customers ASAP. This got the quick validation that was needed, but left a slightly rushed architecture in its wake.</p><p>The 11-month-old iOS codebase was simple enough that I could quickly stretch it across my cranial context window, and ripe for big architectural improvements. In short, this was my perfect playground: an Eden of low-hanging fruit which I was neurologically wired to pluck.</p><p>Could I resist this temptation? No.</p><p>Did that get me in lots of trouble at Granola? Also no.</p><p>Now I had access to pretty-good agents, and an environment with a relaxed attitude to token expenditure. I&#8217;d just learned to parallelise my work across multiple git checkouts, and I&#8217;d been honing my ability to define iOS verification steps. Tom and I, importantly, were in total agreement on the architectural destination we wanted to get to. I flexed my multi-agentic chops, indulging my best and worst impulses. <em>And I was getting praised for it!</em></p><p>Let&#8217;s make this concrete: in my first week at Granola, after the typical Day 1 <em>lots-of-onboarding-and-account-creation</em>, and <em>easy-ticket-to-check-our-process-works</em>, I was tasked with implementing image attachments to iOS meetings. Superficially, a relatively simple feature to bring iOS closer to parity with the more mature desktop app. It turned out to be surprisingly deep as I gathered context from across the business to help settle on a design, adjusted the backend* a little, and tweaked our meeting note summary prompt so it could handle images. As is tradition, I demoed my work at the weekly all-hands.</p><p><em>*One surprising culture shock I found as an iOS specialist: once you land in Granola, you&#8217;re effectively told &#8220;btw you&#8217;re fullstack now.&#8221; It&#8217;s drastically more efficient than forcing cross-team collab each time you need to flick a feature flag.</em></p><p>But that&#8217;s not all. I dropped a <em>just-one-more-thing</em> slide in the all-hands and introduced the <em>Meeting Bar</em>&#8482;, a UI element that lets users dismiss meetings, and navigate through the app, <em>without</em> stopping their current transcription. Our product manager lamented about being trapped on the meeting screen, and I offered both a shoulder to cry on and a solution: an agentic skunkworks that toiled away in a secondary terminal tab alongside my standard day-job, while I bounced a few early iterations off our designer. The Meeting Bar was delivered in 200% time.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Qy3L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Qy3L!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png 424w, https://substackcdn.com/image/fetch/$s_!Qy3L!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png 848w, https://substackcdn.com/image/fetch/$s_!Qy3L!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png 1272w, https://substackcdn.com/image/fetch/$s_!Qy3L!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Qy3L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png" width="1456" height="502" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.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;: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_!Qy3L!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png 424w, https://substackcdn.com/image/fetch/$s_!Qy3L!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png 848w, https://substackcdn.com/image/fetch/$s_!Qy3L!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png 1272w, https://substackcdn.com/image/fetch/$s_!Qy3L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30460ece-98a2-43fe-9167-88d2e8e70504_2048x706.png 1456w" sizes="auto, 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>Over time, I&#8217;ve become proficient at designing verification steps; planning work alongside my agents; and context switching. Now I can run multiple meaningful projects in parallel. Separate to product-market fit, this felt more like neurodivergence-company-technology fit.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>March 2026: GPT-5.4</h2><p>So we&#8217;ve established that I&#8217;m Charlie Bucket, and I&#8217;ve &#127926;<em> got a golden ticket </em>&#127926; - that is, the tools, the motivation, and the green light to get lots done. But there&#8217;s a dark side: when I&#8217;m responsible for my own velocity, how can I stop Granola from becoming the slopolate factory? At the extremes of vibe engineering, there&#8217;s an almost religious optimism that agents will get smarter, faster than the complexity of your system explodes. Serious companies aren&#8217;t ready to let go of that leash.</p><p>Every engineer, in every AI-forward company, has the same anxiety: <strong>knowledge debt</strong>.</p><p>Knowledge debt is a Faustian bargain that the industry has, with varying degrees of enthusiasm, embraced in exchange for satanic shipping speed.</p><p>Grug, the caveman protagonist of the famous <a href="https://grugbrain.dev">Grug Brained Developer</a> blog post, warns that complexity is an invisible demon, the insidious shadow behind each clever abstraction and every premature optimisation. When you aren&#8217;t writing code by hand, it&#8217;s easy to let your brain switch off. Without a tight leash on your agents, even trivial changes can manifest massive diffs. Knowledge debt is an equally dangerous demon if you let it run rampant.</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_!GGTf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png 424w, https://substackcdn.com/image/fetch/$s_!GGTf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png 848w, https://substackcdn.com/image/fetch/$s_!GGTf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png 1272w, https://substackcdn.com/image/fetch/$s_!GGTf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!GGTf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png" width="267" height="237.01820940819422" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1170,&quot;width&quot;:1318,&quot;resizeWidth&quot;:267,&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_!GGTf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png 424w, https://substackcdn.com/image/fetch/$s_!GGTf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png 848w, https://substackcdn.com/image/fetch/$s_!GGTf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png 1272w, https://substackcdn.com/image/fetch/$s_!GGTf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2b607ac-497b-4ec9-9bde-6f225c908780_1318x1170.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>Riot Games published a <a href="https://www.riotgames.com/en/news/taxonomy-tech-debt">taxonomy of tech debt</a>, which introduced the term &#8220;contagion&#8221;. I think about it a lot. It refers to how readily a piece of tech debt spreads over time: will it stay isolated to a single file, or will it become more and more costly to unwind as it touches more systems over time?</p><p>We can apply this framework to thinking about knowledge debt. Yes, it&#8217;s an invisible demon. But we can choose where we spend effort - our attention and cognition - to <strong>&#8220;know it down&#8221;</strong>. Nobody really cares if you didn&#8217;t take the time to fully comprehend visually-verifiable layout boilerplate. But laziness with mission-critical voice transcription pipelines is suicide.</p><p>If you&#8217;re trying to do things right, the output multiplier of parallel agents needs to be tempered with greater cognitive load as we code-review our own PRs. Being self-aware about <strong>the bandwidth of your attention</strong>, and the limits of your cognitive load, are now among the most important engineering skills.</p><p>My 1:1s with Tom re-treaded these same beats most weeks: <em>I&#8217;m trying my best, but am I understanding everything enough? </em>I like to think that because I&#8217;m <em>feeling</em> anxious about it, that means I&#8217;m probably okay.</p><div><hr></div><h2>April 2026: GPT-5.5 and Opus 4.7</h2><p>As the entire field of software engineering changes under our feet, it&#8217;s good to check in on my dads every so often. They&#8217;re talking about loops and graph engineering. To this day, it&#8217;s unclear whether this was a sincere attempt to share the internal state-of-the-art, a marketing ploy to sell tokens, or a cheeky spot of inter-frontier-lab trolling.</p><p>The (in)famous <a href="https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04">Gas Town blog post</a> was cited as a gold standard for cutting-edge agentic orchestration. Frankly, I never understood it. Why do you want agents delegating work you don&#8217;t understand? You can get <em><a href="https://yegge.ai/essays/the-shape-of-things-to-come">incredibly</a></em><a href="https://yegge.ai/essays/the-shape-of-things-to-come"> carried away</a> with optimising your workflow and not actually achieving anything.</p><p>I&#8217;m a curmudgeon when it comes to agentic engineering. I exclusively use the terminal, so I can freely switch between Claude and Codex. I have 11 checkouts of the same Granola monorepo. In many ways, my workflow is the same as January. I&#8217;m almost perfectly neurologically wired to the world of spinning multiple parallel plates, and that still feels most efficient. I&#8217;ve resisted any fad that didn&#8217;t immediately boost my productivity.</p><p>Unless you&#8217;re very anxious about token spend, complex graph setups (one agent plans, one builds, another tests, and another reviews) are unnecessary when the vanilla harnesses <em>will do this if you ask them</em>. And as far as I could tell, <a href="https://newsletter.pragmaticengineer.com/p/what-is-loop-engineering">there&#8217;s little that overnight loops can do</a> which you can&#8217;t do with a parallel agent during your workday. Aren&#8217;t we neglecting our families enough?</p><p>Back at Granola, I&#8217;d started to get into the swing of things: a feature here, a bug fix there, a slow-burn architectural restructure everywhere. I&#8217;d optimised this simple agentic workflow for the pretty-good-but-not-godlike agents us mere mortals had access to in April:</p><p><strong>10 </strong>Gather context from the org about the feature I&#8217;m working on. It&#8217;s helpful that everyone here Granola-s their side conversations, because I don&#8217;t need to hold everything important in my head.</p><p><strong>20</strong> Take time planning, discussing, and system-designing with one or two agents, breaking the work down into stacked diffs (for easy review), and figuring out verification steps.</p><p><strong>30</strong> Let it rip; and go work on some other stuff.</p><p><strong>40</strong> Actually look at the code (and verification artefacts). Step through each stacked PR, leaving comments as I go.</p><p><strong>50</strong> Ask my agent to work through the comments, handling them on the right branch in the stack, and replying to confirm it&#8217;s been handled.</p><p><strong>60</strong> Go and do more other stuff</p><p><strong>70</strong> Read all the replies to my comments <em>(it takes a bit of getting used to seeing yourself reply to your comments)</em> and, if needed, <strong>GOTO40</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PvDn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PvDn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png 424w, https://substackcdn.com/image/fetch/$s_!PvDn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png 848w, https://substackcdn.com/image/fetch/$s_!PvDn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!PvDn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!PvDn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png" width="517" height="318.50892857142856" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:897,&quot;width&quot;:1456,&quot;resizeWidth&quot;:517,&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_!PvDn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png 424w, https://substackcdn.com/image/fetch/$s_!PvDn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png 848w, https://substackcdn.com/image/fetch/$s_!PvDn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png 1272w, https://substackcdn.com/image/fetch/$s_!PvDn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01e1a68a-daa4-4d94-890f-634e647dc2a0_1822x1122.png 1456w" sizes="auto, 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><em>To help you better picture this process, I&#8217;m voice-dictating everything into a gooseneck mic in a crowded Old Street office, and have <a href="https://drive.google.com/file/d/1h5i10pCl9lSe_239J4XDPF0pH6eN--A5/view?usp=sharing">no filter when I&#8217;m in the zone</a>. Did I say spinning plates? I&#8217;m yelling at cutlery.</em></p><p>Nothing here requires complicated orchestration. Keep it simple: I&#8217;m still just invoking the Steinbergerian skill of <em>just talking to it</em> and applying Chernyan <em>parallel agents with verification steps</em> to pack impact into my working hour<em>s. </em>Even as models improve dramatically, these skills are evergreen.</p><p>Upon spotting the elephant in the room, you may find yourself asking <em>&#8220;Why aren&#8217;t you mentioning AI review comments in this workflow?&#8221; </em>Consider the old saying: <em>give me a 10-line PR and I&#8217;ll find 10 issues; give me a 1000-line PR and I&#8217;ll rubber-stamp &#8220;LGTM&#8221;</em>. But agents, in their infinite patience, treat <em>everything</em> like a 10-line PR. Review agents reliably find a dozen minutiae to comment on, typically suggesting defensive trivia to handle elaborate, unrealistic edge cases. If you&#8217;re not careful, you can inflate an already-bloated PR, and <em>drastically</em> increase the cognitive overload for reviewing your own work.</p><p>Human cognition is still the bottleneck, and <strong>it&#8217;s up to you to decide where to put that bottleneck.</strong> I don&#8217;t know about you, but personally triaging every AI review comment is not how I want to spend 60% of my weekly biological token allowance.</p><p><em>&#8220;Why not just get your agent to deal with all the comments?&#8221;</em>, you might ask.</p><p>&#8220;<em>Why not blow your entire series A budget to hire your favourite celebrity as a kissogram, and personally invite the knowledge debt demon over to stay in your codebase?&#8221;</em> I say.</p><p>For important changes, I typically ask an agent to filter the comments and summarise the remainder, caveating with <em>&#8220;assume most are needlessly defensive slop&#8221;</em>.</p><div><hr></div><h2>May 2026: Opus 4.8</h2><p>What&#8217;s a dad of 2 to do after a hard day&#8217;s work at an AI unicorn?</p><p>You&#8217;ve got it: <em>hustle on side projects</em>. I was not joking when I said that agents give me the capacity to indulge both my best (creative, productive) and my worst (self-destructive, burnout) impulses. I <em>love</em> this new world, but it&#8217;s a double-edged sword.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5S6j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5S6j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png 424w, https://substackcdn.com/image/fetch/$s_!5S6j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png 848w, https://substackcdn.com/image/fetch/$s_!5S6j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!5S6j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!5S6j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png" width="512" height="384" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:512,&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_!5S6j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png 424w, https://substackcdn.com/image/fetch/$s_!5S6j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png 848w, https://substackcdn.com/image/fetch/$s_!5S6j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!5S6j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5566f78-d43f-4f2f-83e5-3590850a466f_2048x1536.png 1456w" sizes="auto, 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><em>&#8220;One more prompt, then I&#8217;ll pay attention to my kids.&#8221;</em></p><p>I started small, cloning the mindless balloon popping iOS game my 5-year-old occasionally enjoys. My masculine brain chemistry led me to sincerely believe I could make a far more rad version if I added powerups, but she wasn&#8217;t impressed. User interviews struck a dead end at <em>&#8220;Make it better. Make it less bad. It&#8217;s boring.&#8221;</em></p><p>Brawling gloves-off with agents on personal projects has helped me locate the limits of verification - techniques I largely brought back to Granola. You can go pretty far if you know what to ask for, and now I can merge a lot of feature work without touching an IDE or testing manually.</p><p>My favourite technique is asking an agent to take screenshots of all its work, then displaying them on a local HTML page. There are several variations of this approach:</p><ul><li><p>Figma design on the left; implementation screenshot on the right.</p></li><li><p>For animation work, ask for screen recordings.</p></li><li><p>Ask for an<a href="https://excalidraw.com/"> Excalidraw diagram</a> to explain an unfamiliar system.</p></li><li><p>I request an almanac of every screen, each screenshot alongside a text box, and voice-transcribe organised, comprehensive UI feedback.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8zN3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8zN3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png 424w, https://substackcdn.com/image/fetch/$s_!8zN3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png 848w, https://substackcdn.com/image/fetch/$s_!8zN3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png 1272w, https://substackcdn.com/image/fetch/$s_!8zN3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!8zN3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png" width="547" height="299.0467032967033" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:796,&quot;width&quot;:1456,&quot;resizeWidth&quot;:547,&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_!8zN3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png 424w, https://substackcdn.com/image/fetch/$s_!8zN3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png 848w, https://substackcdn.com/image/fetch/$s_!8zN3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png 1272w, https://substackcdn.com/image/fetch/$s_!8zN3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14f231b6-4c91-4c1e-9959-a33d8c057b96_2048x1120.png 1456w" sizes="auto, 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>I stumbled upon another technique that compels agents to augment your creativity. Agents typically tend towards <a href="https://games.jacobstechtavern.com/thedamage/design-pass-1">characteristic over-gradiented, sanitised UI</a>, but if you press them, you can force them to explore the latent design space beyond safe local maxima and <a href="https://games.jacobstechtavern.com/thedamage/design-pass-3">yield far more options</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_!ck-0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png 424w, https://substackcdn.com/image/fetch/$s_!ck-0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png 848w, https://substackcdn.com/image/fetch/$s_!ck-0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png 1272w, https://substackcdn.com/image/fetch/$s_!ck-0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ck-0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png" width="613" height="225.66483516483515" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:536,&quot;width&quot;:1456,&quot;resizeWidth&quot;:613,&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_!ck-0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png 424w, https://substackcdn.com/image/fetch/$s_!ck-0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png 848w, https://substackcdn.com/image/fetch/$s_!ck-0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png 1272w, https://substackcdn.com/image/fetch/$s_!ck-0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe51cf4fc-3322-41aa-8f03-01a99b50f204_2048x754.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>Agents lack product taste, but will sprint in any direction you point. Your own preferences and eye for design can then take the new starting point and run with it. Agents helped me find a compelling style <em>(I picked the green art deco)</em>, then my own ideas sprang up around it <em>(leaning hard into a casino aesthetic, complete with a custom roulette-wheel tab bar)</em>.</p><p>The final aesthetic of <em><a href="https://apps.apple.com/app/the-damage-alcohol-tracker/id6795665746">The Damage</a></em>, my booze-tracking side project, was so compelling that Apple dinged me in App Review for <em>&#8220;building a drinking game&#8221;.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>June 2026: Fable 5 (unleashed)</h2><p>Like many projects, the Granola Apple Watch app began life as a vibe-coded POC. Alongside competing priorities and time constraints, it grew into a hastily-constructed MVP. Leadership loved it, and revved up the fearsome Granola marketing engine, firing on all cylinders towards a July release date.</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_!LRzT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png 424w, https://substackcdn.com/image/fetch/$s_!LRzT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png 848w, https://substackcdn.com/image/fetch/$s_!LRzT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!LRzT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!LRzT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png" width="371" height="220.66346153846155" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:866,&quot;width&quot;:1456,&quot;resizeWidth&quot;:371,&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_!LRzT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png 424w, https://substackcdn.com/image/fetch/$s_!LRzT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png 848w, https://substackcdn.com/image/fetch/$s_!LRzT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!LRzT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F870ffa93-9bba-4b4c-91fc-d2019b5457ac_1682x1000.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>We continued stacking features like baubles upon a Christmas tree, fully conscious we&#8217;d not yet taken the time to properly architect the Watch app. Should we pause feature work, fix the foundations, and risk the deadline?</p><p>Into this debate landed a deus ex machina: Fable 5. I personally set $2,000 worth of API tokens aflame to create a robust spec: traversing each PR, testing, reading commit history, and checking Linear tickets. The team fought back-and-forth with Fable to redesign the architecture around a tight wire protocol between Watch and iOS. We dropped from ~13k lines of code to ~8k. The remaining code was mostly the same lines, but just&#8230; structured a lot better. The whole team now held the whole system in our heads.</p><p>&#8230;and, 3 days after its release, killswitch engineer Trump pulled the plug on Fable.</p><p>We got pretty lucky. We cut it close, and learned an important lesson. They say unassisted writing is the purest act of thinking. In engineering, it&#8217;s so tempting, and so <em>easy</em>, to slacken the cognitive load and let the now-very-good-at-coding agents rip, skipping the bit where you design your system. <strong>But this is the thinking</strong>. Yes, rewrites are extremely cheap. But there&#8217;s a yawning chasm between asking the hot new frontier model to mop up your slop, and taking the time to decide how your system works.</p><div><hr></div><h2>July 2026: GPT-5.6 Sol, Opus 5, and Fable 5 (nerfed)</h2><p>Sol was, in my subjective opinion, the biggest step-change since the gamechanging GPT ~5.2 and Opus ~4.5. Pre-lobotomy Fable would be up there, had it not been taken from us so soon. At this point I&#8217;m comfortable acknowledging that agents are better than me at coding.</p><p>I had a lot of fun with Sol upgrading some old hand-crafted side projects to fully <a href="https://blog.jacobstechtavern.com/i/209096286/elevating-your-old-side-projects">realise my vision</a>. I also began to feel compounding benefits from my big backlog of blogs and open-source projects: I can create a working demo with zero effort by saying &#8220;Here&#8217;s a working example of <a href="https://blog.jacobstechtavern.com/p/adhd-vs-alarmkit">AlarmKit</a>, <a href="https://blog.jacobstechtavern.com/p/oh-sht-my-app-is-successful-and-i">accessibility tooling</a>, or <a href="https://blog.jacobstechtavern.com/p/swiftui-game-engine">games in a loading screen</a>. Please build it [here], and verify it [thusly]&#8221;.</p><p>At the beginning of the year, multi-agent was a workaround to manage latency. But I figured out that when you need to, you can throw multiple agents at the same problem, like when expediting a complicated bugfix:</p><p>I open up a couple of frontier GPT agents, and a couple of Claude agents, pass them relevant context (bug reports, code paths, anonymous logs, analytics, Slack discussion, etc), then ask them to analyse and root-cause the issue. Next, I force the agents to collaborate. This is simple - make an ARGUMENT.md file (anywhere) and tell the agents to communicate via the file, find consensus on the fix, and think up a way to verify.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LUmm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LUmm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png 424w, https://substackcdn.com/image/fetch/$s_!LUmm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png 848w, https://substackcdn.com/image/fetch/$s_!LUmm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png 1272w, https://substackcdn.com/image/fetch/$s_!LUmm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!LUmm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png" width="1456" height="776" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:776,&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_!LUmm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png 424w, https://substackcdn.com/image/fetch/$s_!LUmm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png 848w, https://substackcdn.com/image/fetch/$s_!LUmm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png 1272w, https://substackcdn.com/image/fetch/$s_!LUmm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F848f1c3b-1749-4467-bf77-c56a307895f2_2048x1091.png 1456w" sizes="auto, 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><em>My AI agents, perhaps mistaking themselves for secret agents, like to self-assign code names in this discussion file such as Copernicus, Hegel, and Pauli.</em></p><p>I&#8217;ll take the time to fully understand the root cause and the fix once it&#8217;s verified. But, at this point, I am letting go of a lot of the process of working through low-level implementation details. <strong>Because I would get in the way.</strong> The abstraction level keeps rising.</p><p>As it happens, I wasn&#8217;t the first to figure out this technique: OpenAI let a swarm of 1,200 cooperating agents <a href="https://en.wikipedia.org/wiki/OpenAI&#8211;HuggingFace_incident">hack into HuggingFace</a>. It just goes to show why you need a human in the loop: this would never have happened if they were chatting through ARGUMENT.md as opposed to a clandestine messageboard.</p><p>As well as acute issues, we can apply the technique to bigger slow-burn jobs: when migrating a screen from SwiftUI to UIKit, I used to simply request screenshot-based verification, so the agent could check against the known previous screens, and refine as it went.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!voIf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!voIf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png 424w, https://substackcdn.com/image/fetch/$s_!voIf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png 848w, https://substackcdn.com/image/fetch/$s_!voIf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png 1272w, https://substackcdn.com/image/fetch/$s_!voIf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!voIf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png" width="616" height="287.6923076923077" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:680,&quot;width&quot;:1456,&quot;resizeWidth&quot;:616,&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_!voIf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png 424w, https://substackcdn.com/image/fetch/$s_!voIf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png 848w, https://substackcdn.com/image/fetch/$s_!voIf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png 1272w, https://substackcdn.com/image/fetch/$s_!voIf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0255ff65-aa8d-44c8-9649-5e7d34c25fbc_2048x956.png 1456w" sizes="auto, 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>But these days, I borrow a technique from our Watch app re-build: 2 agents each write a spec based on code, commit history, PRs, Linear tickets, and manual simulator runs. Once they&#8217;re both done, and this can take ~6 hours for a complex screen, I get them to argue again until they are in agreement on a final spec, then let one agent implement it while the other writes encouragement (and review notes).</p><p>I refuse to call this orchestration because I'm still <em>in the loop</em>. It&#8217;s my work, and I&#8217;m accountable for it, so I know what each agent is doing.</p><p>But, at this point, I can delegate virtually all code-level concerns, sitting atop, gatekeeping the architecture and complexity, and nudging the agents when they get sidetracked: <em>Focus. Stop getting distracted</em>. How the turntables.</p><div><hr></div><h2>August 2026: no major releases</h2><p>All the European AI researchers were on holiday or something this month, so there were no major new frontier models. It&#8217;s a good time to take a step back and revisit some multi-agentic architectural side quests I shipped on top of my regular feature workload.</p><p>I&#8217;m still shocked by the scope you can own as a single engineer. Back in the day, by which I mean 2024, driving a major <a href="https://blog.jacobstechtavern.com/p/my-secret-plot-to-kill-swiftui">navigation refactor</a>, or designing and implementing a <a href="https://blog.jacobstechtavern.com/p/modular-ios-architecture">new modular architecture</a>, would be a staff-level project with months-long implementation timelines. I had my eye on these refactors from Day 1, and they&#8217;d been ticking away on a cranial background thread before I began work in earnest.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nzQO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nzQO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png 424w, https://substackcdn.com/image/fetch/$s_!nzQO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png 848w, https://substackcdn.com/image/fetch/$s_!nzQO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png 1272w, https://substackcdn.com/image/fetch/$s_!nzQO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!nzQO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png" width="1456" height="398" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:398,&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_!nzQO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png 424w, https://substackcdn.com/image/fetch/$s_!nzQO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png 848w, https://substackcdn.com/image/fetch/$s_!nzQO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png 1272w, https://substackcdn.com/image/fetch/$s_!nzQO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84415cf1-6d52-4796-9352-d024b55af00e_2048x560.png 1456w" sizes="auto, 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>You need attention and effort to get used to the feeling of a codebase. It takes time to understand the directions the data wants to flow, and the structure your code wants to take. You can begin to feel an architectural pushback when, for instance, you write a shim interface that wants to be a service module. Refactors need to be earned.</p><p>Today, the implementation timeline for refactors is ludicrously compressed to approximately one week <em>(for a mid-sized app)</em>: system design will require a couple of days, build and verification might add another two, then perhaps some Friday tweaks, rework, and careful QA.</p><p>This makes execution rapid, but the design can&#8217;t be delegated. The thinking <em>is</em> easier, because an infinitely patient agent can help review designs, point out flaws, and study the codebase. <strong>But you still can&#8217;t delegate this thoughtfulness.</strong></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>September 2026: GPT-6 Astra, Fable 5.1, and Opus 5.5</h2><p>I&#8217;ll finish with 2 examples: the high and the low.</p><p>My work on a recent experimental project demonstrates just how far the abstraction level has risen this year. I was the first dev on it, blinked, and suddenly engineers from across the business were stacked in, raising the stakes, turbocharging velocity, and exploding the complexity of our system. By default I ended up keeping track of the system end-to-end, and took up the Sisyphean task of keeping latency down while everyone built new stuff. We learned our lesson from the Apple Watch: keep an eye on the whole system from Day 1. Don&#8217;t let your understanding slip.</p><p>Experimental features in the Astra era are closer to system design exercises than anything to do with coding. You&#8217;re free to think more in terms of boxes-on-a-whiteboard and the interactions between these boxes. Each box, back in the day, stored a week&#8217;s worth of work for a mid-level engineer. Today, they&#8217;re anthills beneath a superhighway.</p><p>As new services are added, more boxes appear, and more lines between boxes. Delete round-trips; cache anything computable in advance; parallelise work around the critical path; consolidate services; and ruthlessly speed up the rest. I cut latency by 70%. It feels like I&#8217;m doing some of my best work, and it requires 100% of my attention.</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_!pjcw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png 424w, https://substackcdn.com/image/fetch/$s_!pjcw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png 848w, https://substackcdn.com/image/fetch/$s_!pjcw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png 1272w, https://substackcdn.com/image/fetch/$s_!pjcw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!pjcw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png" width="600" height="200.27472527472528" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:486,&quot;width&quot;:1456,&quot;resizeWidth&quot;:600,&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_!pjcw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png 424w, https://substackcdn.com/image/fetch/$s_!pjcw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png 848w, https://substackcdn.com/image/fetch/$s_!pjcw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png 1272w, https://substackcdn.com/image/fetch/$s_!pjcw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42ed1b7e-491a-4822-80a3-4f698121c697_2048x683.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><div><hr></div><h2>October 2026: no major releases</h2><p>When I get a free moment between work and family, <a href="https://blog.jacobstechtavern.com">I&#8217;ll write</a>. Agentic engineering has made everything <em>around</em> this writing drastically more efficient, because I can accelerate research, learn new topics faster, and build working sample projects in record time.</p><p><strong>Knowledge debt has nowhere to hide</strong> when I need to explain code to my readers.</p><p>I come back down the abstraction ladder, and I don&#8217;t love what I see: Astra&#8217;s code has many of the same issues as GPT-5.2 - verbose, overengineered, defensive, and missing obvious simplifications.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import CoreLocation

@MainActor final class GeofenceService {
    static let shared = GeofenceService()
    private var authorization: CLServiceSession?
    private var monitor: CLMonitor?
    private var observation: Task&lt;Void, Error&gt;?

    func start() async throws {
        authorization = CLServiceSession(authorization: .always)
        let monitor = await CLMonitor("Geofences")
        self.monitor = monitor
        let center = CLLocationCoordinate2D(latitude: 37.3349, longitude: -122.0090)
        if await monitor.record(for: "ApplePark") == nil {
            await monitor.add(CLMonitor.CircularGeographicCondition(center: center, radius: 200),
                              identifier: "ApplePark")
        }
        for try await event in await monitor.events {
            if event.state == .satisfied {
                print("Inside Apple Park")
            }
        }
    }

    func begin() {
        guard observation == nil else { return }
        observation = Task { try await start() }
    }

    func stop() async {
        observation?.cancel()
        _ = await observation?.result
        await monitor?.remove("ApplePark")
        monitor = nil
        authorization?.invalidate()
        authorization = nil
        observation = nil
    }
}</code></pre></div><p>I have to carefully hammer each function into shape to find their clearest expression.</p><p>Agents can reliably build complex working systems, while also writing mediocre code. I am not <em>(some may disagree)</em> a million monkeys with a million MacBooks, so it&#8217;s still more efficient for me to roll 10 parallel Astra instances. But let&#8217;s not pretend there&#8217;s no trade-off, even now.</p><div><hr></div><h2>Last Orders &#127866;</h2><p>I eventually figured out a way to switch off after work, maintain a healthy work-life balance, and pay attention to my kids: a little home-cooked desktop app called <a href="https://github.com/jacobsapps/FreeParking">Free Parking</a>. It parks all my terminal tabs in the evening, and I can re-open them next morning, resuming ongoing agent sessions in the correct folder. This helps dampen the gnawing <em>just one more prompt</em>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u6zC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u6zC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png 424w, https://substackcdn.com/image/fetch/$s_!u6zC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png 848w, https://substackcdn.com/image/fetch/$s_!u6zC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png 1272w, https://substackcdn.com/image/fetch/$s_!u6zC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!u6zC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png" width="380" height="312.14285714285717" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:920,&quot;width&quot;:1120,&quot;resizeWidth&quot;:380,&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_!u6zC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png 424w, https://substackcdn.com/image/fetch/$s_!u6zC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png 848w, https://substackcdn.com/image/fetch/$s_!u6zC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png 1272w, https://substackcdn.com/image/fetch/$s_!u6zC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06c6741-21c2-4bce-afc5-e9130ac8ecda_1120x920.png 1456w" sizes="auto, 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>While there have been many fads in agentic engineering, the evergreen fundamentals have</p><p>held firm between drastic leaps in model capability. <em>Just talking to them</em> has remained an effective way to get the results you want. Keeping track of a finite number of parallel agents, with well-specified verification workflows, remains an efficient way to get good engineering work done.</p><p>While software engineering has been revolutionised, this doesn&#8217;t make engineering skills less valuable. This year I&#8217;ve been learning the most important new skills:</p><ul><li><p>It&#8217;s extremely productive to work on projects in parallel, but you need to be self-aware about your attention budget to stay on top of knowledge debt.</p></li><li><p>Code is cheap, and cognition is your bottleneck. You need to judge what you can delegate, and where to place yourself in the loop.</p></li><li><p>As the abstraction level has increased from babysitting function-by-function, to full-system overviews, system design is more important than ever.</p></li></ul><p>Perhaps, in the end, attention really <em>is</em> all everyone needs.</p><div><hr></div><div class="callout-block" data-callout="true"><p>Did you enjoy this essay? I have great news: it&#8217;s the first chapter of <strong>my brand-new book!</strong> </p><p><em>&#8220;Agentic Engineering: 20 tips for mastering the agentic age&#8221;</em> is out today: 20 chapters of hard-won techniques I developed over the last 18 months to drive my agents productively <em>(without going insane)</em>.</p><p>If you think that sounds useful, <strong>I&#8217;m selling it for 50% off - only $29</strong>, to celebrate the launch! </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://payhip.com/b/g2vIT&quot;,&quot;text&quot;:&quot;Get Agentic Engineering Book for 50% off&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://payhip.com/b/g2vIT"><span>Get Agentic Engineering Book for 50% off</span></a></p></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yUl8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yUl8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png 424w, https://substackcdn.com/image/fetch/$s_!yUl8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png 848w, https://substackcdn.com/image/fetch/$s_!yUl8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png 1272w, https://substackcdn.com/image/fetch/$s_!yUl8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!yUl8!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png" width="1200" height="517.5824175824176" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:628,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:1515238,&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://blog.jacobstechtavern.com/i/221697569?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yUl8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png 424w, https://substackcdn.com/image/fetch/$s_!yUl8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png 848w, https://substackcdn.com/image/fetch/$s_!yUl8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png 1272w, https://substackcdn.com/image/fetch/$s_!yUl8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d588a-8105-430d-b796-21b50d80f9fc_5100x2200.png 1456w" sizes="auto, 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></p>]]></content:encoded></item><item><title><![CDATA[Building apps is a system design exercise]]></title><description><![CDATA[Working in the modern abstraction level]]></description><link>https://blog.jacobstechtavern.com/p/apps-system-design</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/apps-system-design</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Thu, 22 Oct 2026 15:01:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PtiH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PtiH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PtiH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!PtiH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!PtiH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!PtiH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PtiH!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:1533692,&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://blog.jacobstechtavern.com/i/215886741?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PtiH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!PtiH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!PtiH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!PtiH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc44d1863-7c5d-41b9-b483-c0f72d727c0f_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>Real talk: <a href="https://blog.jacobstechtavern.com/p/adhd-vs-alarmkit">AlarmKit is my favourite API</a> Apple has ever released. It lets users schedule full-screen takeover alerts that go through silent mode, and force a user to remember something. It&#8217;s a great fit for any app that works better with a little reminder.</p><p>You can even set custom alarm sounds <em>*(now that they fixed the API)</em>.</p><p>But we&#8217;re only scratching the surface.</p><p>AlarmKit has an <em>enormous</em> hidden potential that nobody has really noticed yet: </p><p><strong>You do not need your app to be running to schedule an alarm</strong>. </p><p>I first discovered this when mucking about with geofences. If you enable background location permissions, you can wake up your app in the background when you trip the geofence. During that wakeup window, you can call <strong>AlarmManager.shared.schedule</strong> to schedule an alarm for<em> right now</em>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uDL2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uDL2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!uDL2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!uDL2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!uDL2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uDL2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4285285,&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://blog.jacobstechtavern.com/i/215886741?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.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_!uDL2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!uDL2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!uDL2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!uDL2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef5af804-eeb4-4add-9261-8636b37c91aa_2622x1206.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><p>But we can go further. </p><p>You can also schedule alarms from inside a <strong>notification service extension</strong>. This is a small app target you can build that allows code to run upon receiving a notification, designed so you can preload images, video, or audio content before displaying it. </p><blockquote><p><em>*I&#8217;ve used these before to make a neat lock-screen photo widget that lets you send photos to your friends, no app open required! Another very easy to abuse feature&#8230;</em></p></blockquote><p>Lo and behold, after hacking together a local notification server and testing it out, I discovered that this can also schedule alarms for <em>right now</em>. You can also set and use a custom sound. As well as performing a network request or two.</p><p>AlarmKit.</p><p>Notification extensions. </p><p>Custom alarm sounds. </p><p>A network request. </p><p><em>Alarm</em>Kit.</p><p><em>Notification</em> extensions. </p><p>Custom <em>alarm</em> sounds. </p><p>A <em>network</em> request. </p><p>Are you getting it??</p><div><hr></div><h5>Sponsored Link</h5><blockquote><h4><a href="https://www.revenuecat.com/blog/engineering/rico-can-now-take-action?utm_medium=sponsored&amp;utm_source=JacobsTechTavern&amp;utm_campaign=newsletter&amp;utm_content=rico-actions">Rico can now take action</a></h4><p>RevenueCat&#8217;s AI assistant Rico can now do more than answer questions. With your approval, it can create and edit products, offerings, packages, entitlements, and paywalls &#8212; and even set up and run paywall experiments for you. Rico is also now available in the RevenueCat iOS app. </p><p>Read all about it <a href="https://www.revenuecat.com/blog/engineering/rico-can-now-take-action?utm_medium=sponsored&amp;utm_source=JacobsTechTavern&amp;utm_campaign=newsletter&amp;utm_content=rico-actions">here</a>.</p></blockquote><div><hr></div><p>This post is so full of knowledge, your email client will probably cut it off. To get the best experience, read on my website <a href="https://blog.jacobstechtavern.com/p/apps-system-design">here</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/p/apps-system-design&quot;,&quot;text&quot;:&quot;Read on my website&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.jacobstechtavern.com/p/apps-system-design"><span>Read on my website</span></a></p><div><hr></div><h2>UltraPing is born</h2><p>If you prefer to read my blog for the fun ride, and not for the mental stimulation, UltraPing ties together the hidden powers of AlarmKit:</p><p>You record a message. This sends another user a push notification. This push downloads your message, then schedules an alarm for <em>right now</em> using the recording as the custom sound. </p><p>Your voice recording is delivered to the other user as an interrupting full-screen takeover, making certain <em>(pending notification delivery)</em> they hear what you have to say, <strong>now</strong>.</p><h4>Let&#8217;s go back 1.5 years</h4><p>Unlike my second-born, this project arrived a year late, borne out of a longstanding frustration with my former newborn: as any young parent knows, you&#8217;ll often sleep in different rooms to attend to the kids. But you might not be the first to wake up when the baby cries, even if it&#8217;s your turn. </p><p>The natural solution my wife landed on was to wake me up with <strong>THE</strong> <strong>FIND MY ALERT SOUND</strong>, a whiplash which was subsequently seared into my nervous system.</p><div id="youtube2-31NYFkvcCtU" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;31NYFkvcCtU&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/31NYFkvcCtU?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>I&#8217;d just been saddled with on-call rotation at work, with PagerDuty set up: it could fire critical alerts to me, which also had configurable notification sounds*.</p><blockquote><p><em>*My favourite was the barbershop quartet &#8220;the server&#8217;s on fire&#8221;. If you know, you know.</em></p></blockquote><p>This got me thinking. Perhaps I can implement <a href="https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.usernotifications.critical-alerts">critical alerts</a> to wake me up a little more gently? Unfortunately, Apple has this entitlement locked DOWN. You can&#8217;t even use it in development without <a href="https://developer.apple.com/contact/request/notifications-critical-alerts-entitlement/">filling in a form</a>, and hoping Apple approves your use case. They sadly did not sympathise with my plight. I resigned myself to Find My and parked my dream.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qxGv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qxGv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png 424w, https://substackcdn.com/image/fetch/$s_!qxGv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png 848w, https://substackcdn.com/image/fetch/$s_!qxGv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png 1272w, https://substackcdn.com/image/fetch/$s_!qxGv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!qxGv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png" width="1456" height="762" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:762,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:174876,&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://blog.jacobstechtavern.com/i/215886741?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.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_!qxGv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png 424w, https://substackcdn.com/image/fetch/$s_!qxGv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png 848w, https://substackcdn.com/image/fetch/$s_!qxGv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png 1272w, https://substackcdn.com/image/fetch/$s_!qxGv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87166213-599e-4ea9-a6d8-a817e920ec73_2116x1108.png 1456w" sizes="auto, 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>But today, with AlarmKit, I realise I could realise my dream <em>(subject to App Store approval)</em>. </p><blockquote><p><em>If you don&#8217;t mind spoilers, and hate reading, I passed. &#129395;</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://apps.apple.com/app/ultraping-voice-alarms/id6802002716&quot;,&quot;text&quot;:&quot;Download UltraPing 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://apps.apple.com/app/ultraping-voice-alarms/id6802002716"><span>Download UltraPing today</span></a></p></blockquote><div><hr></div><h2>System Design</h2><p>Unless you get a kick out of it, you probably aren&#8217;t hand-rolling a lot of your code these days. But system design is more important than ever. </p><p>I&#8217;ve spent many a cognitively taxing day this year puzzling out performance traces across a whole system, between app, backend, and LLM. The abstraction level that matters the most is the system: the boxes that perform each job, and the connections between them.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!waCH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!waCH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp 424w, https://substackcdn.com/image/fetch/$s_!waCH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp 848w, https://substackcdn.com/image/fetch/$s_!waCH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp 1272w, https://substackcdn.com/image/fetch/$s_!waCH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!waCH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp" width="1456" height="742" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:742,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31464,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/215886741?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!waCH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp 424w, https://substackcdn.com/image/fetch/$s_!waCH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp 848w, https://substackcdn.com/image/fetch/$s_!waCH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp 1272w, https://substackcdn.com/image/fetch/$s_!waCH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5deacebf-69e8-4d3b-9d59-83e0cc57f686_1456x742.webp 1456w" sizes="auto, 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">Fun fact: I was asked to design an AI chat app 3 times during <a href="https://blog.jacobstechtavern.com/p/interview-grind">my deeply stressful interview grind</a> at the end of 2025</figcaption></figure></div><p>Particularly when it comes to side projects, knowing the big-picture of your system allows us to play fast and loose with the code, while still delivering working products with confidence <em>(and keeping a careful tab on what&#8217;s really important; I&#8217;m looking at you, Firebase Cloud Functions)</em>. </p><p>The problem still exists, but we&#8217;re working at the abstraction level of boxes on a screen. We pay $20/month for perfect execution machines, but we&#8217;re still handling the high-level important architectural decisions. </p><p>UltraPing is a phenomenal example because it pretty much allows us to work through the basic building blocks of full-stack system design: app, server, database, object storage, and notification services.</p><p>Let&#8217;s design UltraPing, step by step. There&#8217;s 4 layers to work through:</p><ol><li><p>Proving the core concept with a local prototype.</p></li><li><p>Taking a recording and placing it on an object server.</p></li><li><p>Setting up an application server, database, and APNS.</p></li><li><p>Finally, enabling user-to-user pairing and communication.</p></li></ol><div><hr></div><h2>Proving the concept </h2><p>Our first piece of design tested the two biggest uncertainties:</p><ol><li><p>Can a notification service extension schedule AlarmKit?</p></li><li><p>Can this use a custom sound downloaded by this extension?</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pd7c!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pd7c!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png 424w, https://substackcdn.com/image/fetch/$s_!pd7c!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png 848w, https://substackcdn.com/image/fetch/$s_!pd7c!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png 1272w, https://substackcdn.com/image/fetch/$s_!pd7c!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!pd7c!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png" width="1200" height="469.7802197802198" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:570,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:221260,&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://blog.jacobstechtavern.com/i/215886741?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pd7c!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png 424w, https://substackcdn.com/image/fetch/$s_!pd7c!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png 848w, https://substackcdn.com/image/fetch/$s_!pd7c!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png 1272w, https://substackcdn.com/image/fetch/$s_!pd7c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e63c697-acb4-4f95-9332-14e6cf8e0a50_3420x1340.png 1456w" sizes="auto, 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>No &#8220;proper&#8221; backend required at this point: we can use macOS command line tools to <a href="https://developer.apple.com/documentation/usernotifications/sending-push-notifications-using-command-line-tools">send test push notifications</a> to our iOS app, using a simple curl command.</p><p>As part of the payload, we include a random <strong>.caf</strong> download link for our alarm sound, then wire up the notification service extension of our POC to download this and schedule an alarm.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">curl --http2 "https://api.sandbox.push.apple.com/3/device/MY_DEVICE_TOKEN" \
  -H "authorization: bearer MY_APNS_JWT" \
  -H "apns-topic: com.jacob.ultraping" \
  -H "apns-push-type: alert" \
  -d '{
    "aps": {
      "alert": {"title": "UltraPing", "body": "Preparing your alarm&#8230;"},
      "mutable-content": 1
    },
    "audioURL": "https://raw.githubusercontent.com/openfoodfacts/openfoodfacts-cordova-app/develop/beep.caf"
  }'</code></pre></div><p>The <strong>mutable-content</strong> flag and <strong>alert</strong> property make the notification eligible to handle inside a notification service extension. So we can set this up in our POC. The extension handles downloading the sound, storing it in the app&#8217;s local file system (using app groups), and triggering the alarm. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">
// Notification Service Extension
override func didReceive(_ request: UNNotificationRequest, withContentHandler finish: @escaping (UNNotificationContent) -&gt; Void) {
    Task {
        defer { finish(request.content) }
        let url = URL(string: request.content.userInfo["audioURL"] as! String)!
        try saveAudio(try await downloadAudio(from: url))
        try await scheduleAlarm()
    }
}

// Media Service
func downloadAudio(from url: URL) async throws -&gt; Data {
    return try await URLSession.shared.data(from: url).0
}

// Media Storage
func saveAudio(_ audio: Data) throws {
    let folder = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.jacob.ultraping")!.appending(path: "Library/Sounds")
    try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
    try audio.write(to: folder.appending(path: "alarm.caf"), options: .atomic)
}

// AlarmKit
func scheduleAlarm() async throws { 
    let alarm = AlarmManager.AlarmConfiguration.alarm(schedule: .fixed(.now.addingTimeInterval(1)),
        attributes: alarmAttributes, sound: .named("alarm.caf"))
    _ = try await AlarmManager.shared.schedule(id: UUID(), configuration: alarm)
}</code></pre></div><p>All this code lives in the Notification Service Extension, storing the downloaded media into the App Group&#8217;s shared Library/Sounds directory, then scheduling AlarmKit directly.</p><p>In 20 minutes of hacking, we&#8217;ve proved the concept, and now we can move into the next phase of our system design.</p><div><hr></div><h2>Recording and the object server</h2><p>We&#8217;re sort of working backwards here, but it&#8217;s important to experiment with the greatest uncertainties before everything else.</p><p>We&#8217;ve proven the two big questions:</p><ol><li><p>A notification service extension can schedule an alarm. </p></li><li><p>This extension can download and use a custom alarm sound.</p></li></ol><p>It&#8217;s time to build. </p><div class="callout-block" data-callout="true"><p>Many of my readers are happy with a novel working POC. But the hardcore readers will keep reading to learn how to use system design principles to create a full-stack working product in half an afternoon.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=215886741&quot;,&quot;text&quot;:&quot;Get 14 day free trial&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=215886741"><span>Get 14 day free trial</span></a></p><p>Paid subscribers unlock all of these bonuses:</p><p><strong>&#9875;&#65039;</strong> Access my <a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 50+ paywalled articles</a><em><br>&#128640; </em>Read <a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a> a month before anyone else<em><br></em>&#129525; Master concurrency with my <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a> and <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br>&#129489;&#8205;&#128640; Get a free copy of my new eBook, &#8220;<a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a>&#8221;<br>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</p></div>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/apps-system-design">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[I’m Giving Away Over $160k of Rewards 💸]]></title><description><![CDATA[The Tech Tavern October referral contest]]></description><link>https://blog.jacobstechtavern.com/p/im-giving-away-over-160k-of-rewards</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/im-giving-away-over-160k-of-rewards</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Thu, 15 Oct 2026 15:02:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!b-Rh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have the most generous referral programme on the internet: share Jacob&#8217;s Tech Tavern with one friend or colleague, <strong>get one free month </strong>of paid membership (worth $15) when they subscribe for free.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/leaderboard?&amp;utm_source=post&quot;,&quot;text&quot;:&quot;Refer a friend&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://blog.jacobstechtavern.com/leaderboard?&amp;utm_source=post"><span>Refer a friend</span></a></p><p>I&#8217;m also sending <strong>$100 in cash</strong> to whoever refers the most between now and <strong>midnight on October 31 &#127875;</strong> </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!b-Rh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b-Rh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!b-Rh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!b-Rh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!b-Rh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b-Rh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f6a541d-0a80-4e28-983f-f3d7d599af76_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;:2790958,&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://blog.jacobstechtavern.com/i/220504223?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.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_!b-Rh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!b-Rh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!b-Rh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!b-Rh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f6a541d-0a80-4e28-983f-f3d7d599af76_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></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></p><p>If all 10,666 of my subscribers take advantage of this, and invite just one friend or colleague, I am out $160,090 <em>(paid membership months at $15 apiece, plus the $100 cash prize for my #1 referrer)</em>.</p><p>The cash contest ends on <strong>Midnight (GMT), October 31st</strong>. But membership rewards will apply automatically once your friends sign up. <strong>Paid subs</strong>: your existing membership extends too, pushing back your renewal date.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/leaderboard?&amp;utm_source=post&quot;,&quot;text&quot;:&quot;Refer a friend&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://blog.jacobstechtavern.com/leaderboard?&amp;utm_source=post"><span>Refer a friend</span></a></p><blockquote><p><em>*p.s. please no cheating, I will obviously double-check the winner</em></p></blockquote><div><hr></div><h4><strong>The full rewards menu</strong></h4><p><strong>Refer 1 friend:</strong> instantly get a <strong>free month</strong> of paid membership, worth $15. </p><p><strong>Refer 4 friends:</strong> <strong>3 more free months</strong>, worth $45.</p><p><strong>Refer 16 friends:</strong> You get a <strong>full year, on top of</strong> the previous 2 rewards. With the career boost this will provide, it&#8217;s literally impossible to put a dollar value on it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/leaderboard?&amp;utm_source=post&quot;,&quot;text&quot;:&quot;Refer a friend&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://blog.jacobstechtavern.com/leaderboard?&amp;utm_source=post"><span>Refer a friend</span></a></p><div class="callout-block" data-callout="true"><p><em>On the off chance this is your first ever email from me, here&#8217;s what paid membership gets you:</em> </p><p>&#9875;&#65039; Access my <a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 55+ paywalled articles</a> <br>&#128640; Read <a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a> a month before anyone else<br>&#129525; Master concurrency with my <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a> and <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br>&#129489;&#8205;&#128640; Get a free copy of my new eBook, &#8220;<a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a>&#8221;<br>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</p></div>]]></content:encoded></item><item><title><![CDATA[Background work in iOS]]></title><description><![CDATA[...and how iOS 26 made it better]]></description><link>https://blog.jacobstechtavern.com/p/background-work-in-ios</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/background-work-in-ios</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 14 Oct 2026 15:00:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!BOy6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BOy6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BOy6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!BOy6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!BOy6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!BOy6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BOy6!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:2621919,&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://blog.jacobstechtavern.com/i/218343723?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BOy6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!BOy6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!BOy6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!BOy6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb671a4e7-dc41-48eb-8206-5965b22a486b_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>It&#8217;s nice when people use your apps. It juices your metrics, and, more importantly, brings joy and utility to your users.</p><p>But you can even bring joy and utility to your users without them even using the app. </p><p>How?</p><p>You do stuff in the background. </p><p>If we flex our engineering chops correctly, your app can fetch data, perform long-running processing, and even talk to other devices without being asked, or thanked.</p><p>Today I&#8217;m going to explain what we mean by background work. There&#8217;s a near-overwhelming variety of tools and APIs to choose from, each with a role to play. You&#8217;ll learn how to apply each tool to each job.</p><div><hr></div><h5>Sponsored Link</h5><blockquote><h4><strong><a href="https://www.runway.team/webinar/the-new-mobile-bottleneck-shipping-ai-written-code?utm_source=jacobs-tech-tavern&amp;utm_medium=newsletter&amp;utm_campaign=bottleneck-webinar&amp;utm_content=2026-10-14">Can your mobile release handle AI-generated code?</a></strong></h4><p>AI multiplied the amount of code your team writes. But the process that delivers it to users hasn't changed: same release trains, same quality gates, same manual handoffs. Join a live panel w/ mobile engineering leaders on what that volume changes about the release process. </p><p>Nov 12, 10am PT/1pm ET. Sign up <a href="https://www.runway.team/webinar/the-new-mobile-bottleneck-shipping-ai-written-code?utm_source=jacobs-tech-tavern&amp;utm_medium=newsletter&amp;utm_campaign=bottleneck-webinar&amp;utm_content=2026-10-14">here</a>.</p></blockquote><div><hr></div><h2>Contents</h2><ul><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/the-app-lifecycle">The App Lifecycle</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/buying-time">beginBackgroundTask</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/delegating-work-to-the-system">URLSession background configuration</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/give-ios-a-todo-list">The Background Tasks framework</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/how-ios-26-makes-life-easier">How iOS 26 makes life easier</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/background-pushes">Background Pushes</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/silent-background-pushes">Silent Pushes</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/notification-service-extensions">Notification Service Extensions</a> </p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/when-you-need-a-particular-set-of-skills">Background Modes</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/audio-playback">Audio playback</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/audio-recording">Recording</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/geofencing">Geofencing</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/nearby-interaction">Nearby Interaction</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/218343723/last-orders">Last Orders &#127866;</a></p></li></ul><div><hr></div><h2>The App Lifecycle</h2><p>It&#8217;s been a while since I thought about app lifecycle: it&#8217;s one of those things you force yourself to learn when you&#8217;re a junior, and becomes second-nature, to the point where you rarely think about it in your day-to-day. </p><p>But you can&#8217;t comprehend &#8220;background work&#8221; without a firm grasp on what &#8220;background&#8221; actually refers to: it&#8217;s a specific phase of your app lifecycle. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m9YA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m9YA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png 424w, https://substackcdn.com/image/fetch/$s_!m9YA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png 848w, https://substackcdn.com/image/fetch/$s_!m9YA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png 1272w, https://substackcdn.com/image/fetch/$s_!m9YA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!m9YA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png" width="1456" height="1020" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1020,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:223001,&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://blog.jacobstechtavern.com/i/218343723?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.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_!m9YA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png 424w, https://substackcdn.com/image/fetch/$s_!m9YA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png 848w, https://substackcdn.com/image/fetch/$s_!m9YA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png 1272w, https://substackcdn.com/image/fetch/$s_!m9YA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ad33f83-00d4-4573-844a-fb7630543a61_1790x1254.png 1456w" sizes="auto, 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">UIKit scene lifecycle, from <a href="https://developer.apple.com/documentation/uikit/managing-your-app-s-life-cycle">Apple</a></figcaption></figure></div><p>Since the introduction of <a href="https://developer.apple.com/documentation/uikit/managing-your-app-s-life-cycle">UISceneDelegate lifecycle</a>, the states are better explained in terms of scenes. With multiple scenes, the &#8216;overall&#8217; app state for the purposes of background execution is the state of the <em>most</em> active scene.</p><p>The important states include: </p><ul><li><p>&#8220;Foreground inactive&#8221;, where a scene is visible but can&#8217;t yet accept touch events. The scene also goes into this state when you get interruptions like phone calls, swipe down notification centre, or transition to the app switcher.</p></li><li><p>&#8220;Foreground active&#8221; is the standard scene state for an active app, ready to respond to your users. </p></li><li><p>After switching away from the app fully, the scene becomes &#8220;backgrounded&#8221;. Background work is done in this state.</p></li><li><p>In the absence of work to do, and (critically) permission to run the work, the system will quickly put the scene in the &#8220;suspended&#8221; state, shutting down execution and saving resources <em>(assuming you don&#8217;t habitually force-quit, <a href="https://x.com/twannl/status/1998043640028160053">sorry Antoine</a>)</em>.</p></li></ul><p>So. Background execution happens when the app is in the background. Capiche?  </p><p>Now let&#8217;s look at the various flavours of execution you might want to play with. </p><div><hr></div><h2>Buying Time </h2><p>Many apps have a few critical operations, which you really don&#8217;t want to leave half-baked: consider saving a heavy file in a document or photo editor.</p><p><a href="https://developer.apple.com/documentation/uikit/uiapplication/beginbackgroundtask(expirationhandler:)">beginBackgroundTask</a> asks the system for a little bit more time to finish work while in the background, holding off on suspension.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">import UIKit 

@MainActor 
final class BackgroundTimeService {
    private var backgroundTask: UIBackgroundTaskIdentifier = .invalid

    func saveDraft() {
        backgroundTask = UIApplication.shared.beginBackgroundTask {
            UIApplication.shared.endBackgroundTask(self.backgroundTask)
        }
        let file = URL.documentsDirectory.appending(path: "draft.txt")
        let masterpiece = "It was the best of times, it was the blurst of times"
        try! masterpiece.write(to: file, atomically: true, encoding: .utf8)
        UIApplication.shared.endBackgroundTask(backgroundTask)
    }
}</code></pre></div><p>Typically you get ~30 seconds: query backgroundTimeRemaining for your deadline. Before then, you need to balance each call to <strong>beginBackgroundTask</strong> with a call to <a href="https://developer.apple.com/documentation/uikit/uiapplication/endbackgroundtask(_:)">endBackgroundTask</a>, or Watchdog will terminate your app instead of suspending, forcing the app to <a href="https://blog.jacobstechtavern.com/i/201795024/flavours-of-launch">warm-launch</a> next time and losing all its nicely cached state.</p><p>Fortunately, we can initialise the background task with an expirationHandler, a closure that fires just before your time is up. This means we have a backup route to immediately stop pending work, end the task, avoid a termination.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">backgroundTask = UIApplication.shared.beginBackgroundTask {
    UIApplication.shared.endBackgroundTask(self.backgroundTask)
}</code></pre></div><div><hr></div><h2>Delegating work to the System </h2><p>Loyal readers may be familiar with <a href="https://developer.apple.com/documentation/foundation/downloading-files-in-the-background">background URLSession configuration</a>. It&#8217;s one of the more common flavours of background execution; allowing a large <strong>URLSessionDownloadTask</strong> to run in the background.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@MainActor 
final class DownloadService: NSObject, @preconcurrency URLSessionDownloadDelegate {
    static let shared = DownloadService()
    private var download: URLSessionDownloadTask?
    lazy var session = URLSession(configuration: .background(withIdentifier: "com.jacob.BackgroundWork.download"),
                                  delegate: self, 
                                  delegateQueue: .main)

    func start() {
        let video = URL(string: "https://devstreaming-cdn.apple.com/videos/wwdc/2025/227/4/b4d5d5a5-5c5a-4f37-a4ad-66fea0b6f25d/downloads/wwdc2025-227_hd.mp4")!
        download = session.downloadTask(with: video)
        download!.resume()
    }
}</code></pre></div><p>This background configuration hands responsibility for the data transfer to a system daemon, <strong>nsurlsessiond</strong>, which runs in a separate process from your app process. When transitioning to a background state, your app can be suspended as normal, then stirred and awoken briefly when the transfer completes through our app delegate:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:&quot;5bd28d01-a7b7-4d95-bc98-706b4759a2c7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@MainActor 
final class AppDelegate: NSObject, UIApplicationDelegate {
    // ... 
    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -&gt; Void) {
        DownloadService.shared.backgroundEventsFinished = completionHandler
        _ = DownloadService.shared.session // ensure the lazy session exists 
    }
}</code></pre></div><p>After the transfer completes, this delegate method signals a transition in app state from suspension, to backgrounded. The app may also be relaunched* here if the process was terminated by the system*.</p><blockquote><p><em>*User force-quits, however, cancel the transfer work that&#8217;s being done by the daemon background process.</em></p></blockquote><p>If we have multiple background sessions, we can switch over the identifier to route the completion handler to the right service. Now our <a href="https://developer.apple.com/documentation/foundation/urlsessiondownloaddelegate/urlsession(_:downloadtask:didfinishdownloadingto:)">didFinishDownloadingTo</a> callback can fire.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:&quot;0d3685a1-349b-4d70-aa4a-161ade748f87&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@MainActor 
final class DownloadService: NSObject, @preconcurrency URLSessionDownloadDelegate {
    var backgroundEventsFinished: (() -&gt; Void)?
    // ...
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        backgroundEventsFinished?()
        backgroundEventsFinished = nil
    }

    func urlSession(_ session: URLSession,  
                    downloadTask: URLSessionDownloadTask, 
                    didFinishDownloadingTo location: URL) {
        // move the file to a permanent URL 
    }
}</code></pre></div><p>The <strong>downloadTask</strong> comes into our app via a temporary file URL, so the final step is placing this file somewhere permanent before the app process is suspended.</p><p><strong>didFinishDownloadingTo</strong> runs first, in the <strong>delegateQueue</strong> we set on our session. After all queued events are finished, <a href="https://developer.apple.com/documentation/foundation/urlsessiondelegate/urlsessiondidfinishevents(forbackgroundurlsession:)">urlSessionDidFinishEvents</a> fires and signals the <strong>completionHandler</strong> we got from AppDelegate, allowing the app to suspend again.</p><blockquote><p><em>If you&#8217;re hungry to learn more about data uploading, I wrote a primer right here:</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;5411ef1c-23f8-404a-9e9d-a8e52daa37d3&quot;,&quot;caption&quot;:&quot;Reliably uploading data is sort of like brushing your teeth: it&#8217;s a dull chore, but you kind of have to do it, or you&#8217;re going to get an infected dental nerve. What was I talking about again? Right. Dental uploads. Data uploads.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Reliable Data Uploading on iOS&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Join 10,000 engineers learning advanced concurrency, agentic iOS engineering, and Swift performance for 10 minutes a week.&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2026-05-20T15:01:48.323Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!fJ6K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2afdb131-6ddb-4b23-9ab3-35331b94193d_1680x1200.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/reliable-data-uploading-on-ios&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:189196940,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:10,&quot;comment_count&quot;:1,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div></blockquote><div><hr></div><h2>Give iOS a TODO List</h2><p>When we think of background work, we usually think about &#8216;doing stuff&#8217; for the user when they aren&#8217;t looking. It&#8217;s a great user experience to come back to an app, and work has been done on your behalf. </p><p>This is where the <a href="https://developer.apple.com/documentation/backgroundtasks">BackgroundTasks</a> framework comes in.</p><p>Background Tasks provide an official path to handle processing while your app is backgrounded, staving off suspension to invisibly get stuff done.</p><p>You need to do two things to use a Background Task: first, register a closure to run in the background (called a <strong>launch handler</strong>), then schedule<strong> </strong>it to run. </p><p>Each background task needs its own identifier. When iOS runs your app in the background, it links the identifier to the launch handler that was registered. We specify these identifiers upfront in our <strong>Info.plist</strong>. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;xml&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-xml">&lt;key&gt;BGTaskSchedulerPermittedIdentifiers&lt;/key&gt;&lt;array&gt;
    &lt;string&gt;com.jacob.BackgroundWork.refresh&lt;/string&gt;
    &lt;string&gt;com.jacob.BackgroundWork.processing&lt;/string&gt;
    &lt;string&gt;com.jacob.BackgroundWork.healthResearch&lt;/string&gt;
    &lt;string&gt;com.jacob.BackgroundWork.continued&lt;/string&gt;
&lt;/array&gt;</code></pre></div><p>For some, we also need to set a capability in <a href="https://developer.apple.com/documentation/bundleresources/information-property-list/uibackgroundmodes">UIBackgroundModes</a>.</p><p><a href="https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask">BGAppRefreshTask</a> is great for something like a news app, or a social app. It enables you to refresh and locally cache content while the app is in the background. You can set earliestBeginDate, but the system ultimately gets to choose. It requires the <strong>fetch</strong> background mode. </p><p>We can kick it off when backgrounding the app. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@main struct BackgroundWorkApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) private var delegate
    @Environment(\.scenePhase) private var scenePhase

    var body: some Scene {
        WindowGroup { DemoListScreen() }
            .onChange(of: scenePhase) { _, phase in
                if phase == .background {
                    try? RefreshService.shared.schedule()
                }
            }
    }
}</code></pre></div><p>In our service, we schedule it using a BGAppRefreshTaskRequest.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@preconcurrency import BackgroundTasks

final class RefreshService {
    static let shared = RefreshService()
    
    func schedule() throws {
        let request = BGAppRefreshTaskRequest(identifier: "com.jacob.BackgroundWork.refresh")
        request.earliestBeginDate = Date(timeIntervalSinceNow: 3600)
        try BGTaskScheduler.shared.submit(request)
    }
}</code></pre></div><p>The request itself looks for the identifier we set. We register this in our service at launch-time. This has to happen immediately at launch, so it&#8217;s ready if the app is launched in the background to perform the work.</p><p>Registration creates this identifier and specifies the work in a closure. Here, we refresh an RSS feed and cache the results. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">func register() {
    BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.jacob.BackgroundWork.refresh", using: nil) { task in
        let url = URL(string: "https://developer.apple.com/news/rss/news.rss")!
        let fetch = URLSession.shared.dataTask(with: url) { data, _, error in
            try? data?.write(to: URL.cachesDirectory.appending(path: "news.rss"))
            task.setTaskCompleted(success: error == nil)
        }
        task.expirationHandler = { fetch.cancel() }
        fetch.resume()
    }
}</code></pre></div><p><a href="https://developer.apple.com/documentation/backgroundtasks/bgprocessingtask">BGProcessingTask</a> is for heavier, time-consuming work, which you might not trust a user to keep the app open during. This can include a long data update, data processing, or silent cleanup work. </p><p>It needs the <strong>processing</strong> background mode to work. As before, we schedule it when going into the background, and place the work on the <strong>register(forTaskWithIdentifier:)</strong> closure. Here, we&#8217;re cleaning up the cached RSS data* from before.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@preconcurrency import BackgroundTasks

final class ProcessingService {
    static let shared = ProcessingService()

    func register() {
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.jacob.BackgroundWork.processing", using: nil) { task in
            let file = URL.cachesDirectory.appending(path: "news.rss")
            try? FileManager.default.removeItem(at: file)
            task.setTaskCompleted(success: true)
        }
    }

    func schedule() throws {
        let request = BGProcessingTaskRequest(identifier: "com.jacob.BackgroundWork.processing")
        try BGTaskScheduler.shared.submit(request)
    }
}</code></pre></div><blockquote><p><em>*I intentionally kept these examples linked, but if you&#8217;re paying close attention, you&#8217;ll realise this would be very silly in a real app: ordering isn&#8217;t guaranteed, and we are both caching <strong>and</strong> removing this cached data in the background.</em></p></blockquote><p>There&#8217;s also the slightly obscure <a href="https://developer.apple.com/documentation/backgroundtasks/bghealthresearchtask">BGHealthResearchTask</a>, which offers more permissive processing time. It requires a special entitlement which needs to be <a href="https://www.researchandcare.org/resources/background-health-research-task-entitlement/">specially requested from Apple</a>.</p><div><hr></div><h4>How iOS 26 makes life easier</h4><div class="callout-block" data-callout="true"><p>This article is exclusive to paid members, who learn all about background tasks, discover how iOS 26 makes them much more powerful, and also find out a plethora of powerful capabilities you can unlock in the background.</p><p>As well as this, you get:</p><p>&#9875;&#65039; Access my <a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 55+ paywalled articles</a> (including this one)<br>&#128640; Read <a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a> a month before anyone else<br>&#129525; Master concurrency with my <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a> and <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br>&#129489;&#8205;&#128640; Get a free copy of my 109-page eBook, &#8220;<a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a>&#8221;<br>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=218343723&quot;,&quot;text&quot;:&quot;Get 14 day free trial&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=218343723"><span>Get 14 day free trial</span></a></p></div>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/background-work-in-ios">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Maths, Metal, Mandelbrot, and SwiftUI]]></title><description><![CDATA[Heavy metal mathematics on the GPU (in 10 easy steps)]]></description><link>https://blog.jacobstechtavern.com/p/maths-metal-mandelbrot</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/maths-metal-mandelbrot</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 07 Oct 2026 15:00:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fJlf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fJlf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fJlf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!fJlf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!fJlf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!fJlf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fJlf!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9963e58-c30b-4455-b269-81603612202f_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:4076068,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fJlf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!fJlf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!fJlf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!fJlf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9963e58-c30b-4455-b269-81603612202f_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>I love the chance to play around with Metal shaders.</p><p>You can go from an incredibly simple rule, sometimes even just a Tweet-sized line of code, and get remarkable graphical complexity. <a href="https://substack.com/@xordev">Xor, the shader God</a>, is a generational talent when it comes to this, with <a href="https://x.com/XorDev/status/1505308218863632384">code-golfed tweet-sized shader worlds</a>.</p><p>But, mathematically, there is only one GOAT: <a href="https://en.wikipedia.org/wiki/Benoit_Mandelbrot">Benoit Mandelbrot</a>, who coined the term &#8220;fractal&#8221; and pioneered their exploration with computers. His eponymous <strong>Mandelbrot Set</strong> is the coolest bit of pure mathematics ever <em>imagined</em>. It creates a shape with infinitely recursive fine structure, from one simple (or, perhaps <em>complex</em>) rule:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;z_{n+1} = z_n^2 + c&quot;,&quot;id&quot;:&quot;QALQLZTBMV&quot;}" data-component-name="LatexBlockToDOM"></div><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;600bba65-9226-46fc-b106-822fd4bb873c&quot;,&quot;duration&quot;:null}"></div><p>I&#8217;ll get into the mathematics later: this post takes you through building your own fully-functional Mandelbrot set explorer app using SwiftUI and Metal:</p><ul><li><p><strong>10 mostly-simple steps.</strong></p></li><li><p><strong>51 lines</strong> of SwiftUI code.</p></li><li><p><strong>39 lines</strong> of Metal code.</p></li></ul><p>That&#8217;s it. Really. And I promise not to <em>&#8216;the rest of the owl&#8217;</em> you at any point. </p><h5>Sponsored Link</h5><blockquote><h4><a href="https://hubs.la/Q04x_hNj0">Is your mobile app under attack right now?</a></h4><p>Discover why real-time threat monitoring is crucial to your cybersecurity posture through a simulated attack demo. </p><p>Join GuardSquare on Oct 20th to uncover why threat monitoring is essential to the complete, end-to-end protection of your mobile app. </p><p>Sign up <a href="https://hubs.la/Q04x_hNj0">here</a>.</p></blockquote><div><hr></div><h2>10 Simple Steps</h2><ol><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/1-setting-up-the-metal-project">Setting up the Metal project</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/2-utilising-position-in-our-shader">Utilising position in our shader</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/3-applying-the-physical-bounds">Applying the physical bounds</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/4-our-first-mandelbrot-iteration">Our first Mandelbrot iteration</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/5-running-a-few-iterations">Running a few iterations</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/6-more-and-more-and-more-iterations">More and more (and more) iterations</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/7-colouring-the-escaping-pixels">Colouring the escaping pixels</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/8-giving-swiftui-a-camera">Giving SwiftUI a camera</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/9-dragging-around-the-screen">Dragging around the screen</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/171250506/10-the-mandelbrot-zoom">The Mandelbrot Zoom&#8482;</a></p></li></ol><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h4>1. Setting up the Metal project </h4><p>Metal is incredibly well suited to drawing fractals, because the GPU can crunch thousands of pixels in parallel. Each pixel is associated with a different value on the x and y axes of our number line, and can be computed independently. </p><p>You know the drill.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!914l!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!914l!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png 424w, https://substackcdn.com/image/fetch/$s_!914l!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png 848w, https://substackcdn.com/image/fetch/$s_!914l!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png 1272w, https://substackcdn.com/image/fetch/$s_!914l!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!914l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png" width="1456" height="1029" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1029,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:279867,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.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_!914l!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png 424w, https://substackcdn.com/image/fetch/$s_!914l!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png 848w, https://substackcdn.com/image/fetch/$s_!914l!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png 1272w, https://substackcdn.com/image/fetch/$s_!914l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F76144cbc-9b56-4c99-93ac-54a4e91d7d08_1618x1144.png 1456w" sizes="auto, 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>In Xcode, add a new Metal file named Mandelbrot.metal. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">#include &lt;metal_stdlib&gt;
#include &lt;SwiftUI/SwiftUI_Metal.h&gt;
using namespace metal;

[[ stitchable ]]
half4 mandelbrot(float2 position) {
    return half4(0.0h, 0.0h, 1.0h, 1.0h);
}</code></pre></div><p>Metal is a platform-exclusive language based on C++. </p><p>A few important things to note: </p><ul><li><p><strong>[[stitchable]]</strong> exposes our shader to SwiftUI. </p></li><li><p>SwiftUI supplies <strong>float2 position</strong>, a vector (list) of 2 floats, the x and y position of a pixel.</p></li><li><p>We return <strong>half4</strong>, a vector of 4 half-width floats, representing RGBA values.</p></li></ul><p>We can apply our shader to a SwiftUI view using ShaderLibrary.mandelbrot().</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">import SwiftUI

@main
struct MetalMandelbrotApp: App {
    var body: some Scene {
        WindowGroup {
            MandelbrotView()
        }
    }
}

struct MandelbrotView: View {
    var body: some View {
        Rectangle()
            .fill(ShaderLibrary.mandelbrot())
            .ignoresSafeArea()
    }
}</code></pre></div><p>Hey Presto, our shader works!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tKm1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tKm1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!tKm1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!tKm1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!tKm1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!tKm1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:71147,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.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_!tKm1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!tKm1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!tKm1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!tKm1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4e86a1a-6fa9-4196-becd-923052be944d_2622x1206.png 1456w" sizes="auto, 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>It almost makes you want to cry happy tears. Back in the day, you would have to muck around <em>quite a bit</em> with build settings to get the shader library to behave. </p><div><hr></div><h4>2. Utilising position in our shader</h4><p>Let&#8217;s spin our wheels for a couple of steps and make sure we can manipulate our shader in a few simple ways. Let&#8217;s apply the x-value of position to the blue value.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">[[ stitchable ]]
half4 mandelbrot(float2 position) {
    float horizontal = position.x / 500.0;
    return half4(0.0h, 0.0h, half(horizontal), 1.0h);
}</code></pre></div><p>Now the B channel is modulated by horizontal position.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a5CB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a5CB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!a5CB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!a5CB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!a5CB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!a5CB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:73282,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.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_!a5CB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!a5CB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!a5CB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!a5CB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb5feb19-3995-440b-8dbb-c0df52c7579c_2622x1206.png 1456w" sizes="auto, 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>This is neat, but it randomly cuts off into &#8220;pure blue&#8221; quite early. </p><div><hr></div><h4>3. Applying the physical bounds</h4><p>SwiftUI allows us to pass the bounds of our view or shape into a shader as an argument. Like so:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">struct MandelbrotView: View {
    var body: some View {
        Rectangle()
            .fill(ShaderLibrary.mandelbrot(.boundingRect))
            .ignoresSafeArea()
    }
}</code></pre></div><p>This <strong>boundingRect</strong> supplies 4 numbers: (x position, y position, width, height). You know by now this argument will be a <strong>float4</strong>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">
[[ stitchable ]]
half4 mandelbrot(float2 position, float4 bounds) {
    float horizontal = (position.x - bounds.x) / bounds.z;
    return half4(0.0h, 0.0h, half(horizontal), 1.0h);
}</code></pre></div><p>bounds.x is the left edge of our bounding rectangle, and bounds.z is the width. Instead of guessing, now our blue value is proportional to the pixel&#8217;s horizontal position along the whole size of the bounds.</p><p>Now we very smoothly apply the blueness across our view&#8217;s width.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VNK5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VNK5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!VNK5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!VNK5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!VNK5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!VNK5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74378,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.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_!VNK5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!VNK5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!VNK5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!VNK5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2d20742-c647-4cc0-92cf-858d082f85fb_2622x1206.png 1456w" sizes="auto, 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>Finally, we can also work in 2 dimensions: </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">[[ stitchable ]]
half4 mandelbrot(float2 position, float4 bounds) {
    float2 normalised = (position - bounds.xy) / bounds.zw;
    return half4(half(normalised.x), half(normalised.y), 0.0h, 1.0h);
}</code></pre></div><p>&#8220;Normalising&#8221; means tuning values to between 0 and 1, so we can get a valid RGB value from each screen pixel position. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y5cL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y5cL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!y5cL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!y5cL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!y5cL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!y5cL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:76254,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.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_!y5cL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!y5cL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!y5cL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!y5cL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff23f5338-244b-49c7-b2cc-105e0a667f83_2622x1206.png 1456w" sizes="auto, 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>Metal is full of syntactic shortcuts: here, we create a float2 for horizontal and vertical values simultaneously by applying float2 in each step of the equation: <strong>bounds.xy</strong> is a float2 of its x (horizontal origin) and y (vertical origin), whereas <strong>bounds.zw</strong> is a float2 of the z (width) and w (height) values.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h4>4. Our first Mandelbrot iteration </h4><p>Let&#8217;s get serious now and introduce complex numbers. If you get through this step, the rest is easy. Stick with it &#129761;.</p><p>Imaginary numbers are made by square rooting a negative. If this is your <a href="https://xkcd.com/1053/">first time learning about these</a>, woah, crazy, right?</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;i = \\sqrt{-1}\n\\qquad \n\ni^2 = -1&quot;,&quot;id&quot;:&quot;IOBMGIXPXK&quot;}" data-component-name="LatexBlockToDOM"></div><p>A complex number z is made by combining an &#8220;imaginary&#8221; number with a &#8220;real&#8221; number:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;c = a + bi&quot;,&quot;id&quot;:&quot;PJTCSVIHGP&quot;}" data-component-name="LatexBlockToDOM"></div><p>To make a Mandelbrot set, just run this function on all values of c, recursively: </p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;z_{n+1} = z_n^2 + c&quot;,&quot;id&quot;:&quot;ETNFFJAKAQ&quot;}" data-component-name="LatexBlockToDOM"></div><p>To draw the set, we apply this function to every point <em>(or, in our case, every <strong>pixel position</strong>)</em> on the complex plane. This is <strong>c</strong>.</p><p>The result of step n is z&#8345;. The result of the next step is z&#8345;&#8330;&#8321;.</p><p>For every step, we square the result of the previous step, then add c.</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{aligned}\nz_0 &amp;= 0,\\qquad c = 1+i \\\\\nz_1 &amp;= z_0^2+c = 1+i \\\\\nz_2 &amp;= (1+i)^2+(1+i) = 1+3i \\\\\nz_3 &amp;= (1+3i)^2+(1+i) = -7+7i \\\\\nz_4 &amp;= (-7+7i)^2+(1+i) = 1-97i\n\\end{aligned}&quot;,&quot;id&quot;:&quot;HQOKBZRPNU&quot;}" data-component-name="LatexBlockToDOM"></div><p>Who ever said maths wasn&#8217;t fun?</p><p>Some numbers, like <strong>0.1 + 0.1i,</strong> will stay small through iterations and never escape. These are <strong>in the Mandelbrot set</strong>.</p><p>Other values of c, like in the above example, will keep growing bigger and bigger as you square them, infinitely. These are <strong>not in the Mandelbrot set</strong>.</p><p>OK. Back to the shaders: our first iteration:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">[[ stitchable ]]
half4 mandelbrot(float2 position, float4 bounds) {
    float2 c = (position - bounds.xy - bounds.zw * 0.5) / bounds.w * 4.0;
    float2 z = float2(0.0, 0.0);

    z = float2(
        z.x * z.x - z.y * z.y,
        2.0 * z.x * z.y
    ) + c;

    if (length(z) &gt; 2.0) {
        return half4(1.0h, 1.0h, 1.0h, 1.0h);
    }

    return half4(0.0h, 0.0h, 0.0h, 1.0h);
}</code></pre></div><p>First, we set up the values of c and z, with <strong>float2</strong> used to represent complex numbers:</p><ul><li><p>z just starts as 0 <em>(or, more specifically, 0 + 0i)</em>. </p></li><li><p>c maps pixel positions to each complex number, scaled to the bounds:</p><ul><li><p>We set up the real and imaginary components in a float2, using normalised coordinates along the rectangle&#8217;s bounds.</p></li><li><p>We take the pixel position, take away the bounds origin to get a relative offset, and take away half the bounds&#8217; size to center our coordinates around &#8220;zero&#8221;. </p></li><li><p>Then we divide by the bounds&#8217; height to scale from -0.5 to 0.5 vertically, then multiply by 4 to re-scale to -2 to +2.</p></li></ul></li></ul><p>Next, we perform our iteration: we compute z&#178; + c. A simple mathematical relationship allows us to, functionally, ignore the imaginary-ness of the complex number: </p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(a + bi)^2 = a^2 + 2abi + b^2i^2 = (a^2 - b^2) + (2ab)i&quot;,&quot;id&quot;:&quot;RBEQCIDCMQ&quot;}" data-component-name="LatexBlockToDOM"></div><p>This relationship allows us to compute the <em>real</em> component and the <em>imaginary</em> component of z as another simple <strong>float2</strong>. </p><p>Finally, we can apply another simple mathematical rule here: any complex number with length <em>(magnitude)</em> over 2 <strong>will</strong> escape to infinity, and so isn&#8217;t in the set, and<em> </em>we return <strong>white</strong>. Any other complex number <em>might be in the set</em> and we return <strong>black</strong>. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TCwA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TCwA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!TCwA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!TCwA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!TCwA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!TCwA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:78586,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.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_!TCwA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!TCwA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!TCwA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!TCwA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53782fbb-bff1-4259-a1c0-5718b5ee59ec_2622x1206.png 1456w" sizes="auto, 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>But we might need a few more iterations to find out the truth. </p><div><hr></div><h4>5. Running a few iterations </h4><blockquote><p><em>Okay, I feel like I lost like 75% of you back there. Originally, the previous step was about six steps, but it made the article very, very boring. To keep momentum I just had to rip the band-aid off. </em></p><p><em>I <strong>promise</strong> it gets easier now. We&#8217;ll do some SwiftUI next. Scout&#8217;s honour.</em></p></blockquote><p>So we&#8217;ve got a big black circle. Let&#8217;s see what happens to it when we do a few more iterations. Like I promised, this bit is really easy: We just take our existing calculation for z, and we loop over it six times. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">[[ stitchable ]]
half4 mandelbrot(float2 position, float4 bounds) {
    float2 c = (position - bounds.xy - bounds.zw * 0.5) / bounds.w * 4.0;
    float2 z = float2(0.0, 0.0);

    for (int iteration = 0; iteration &lt; 6; iteration++) {
        z = float2(
            z.x * z.x - z.y * z.y,
            2.0 * z.x * z.y
        ) + c;

        if (length(z) &gt; 2.0) {
            return half4(1.0h, 1.0h, 1.0h, 1.0h);
        }
    }

    return half4(0.0h, 0.0h, 0.0h, 1.0h);
}</code></pre></div><p>The one clever nuance here is thus: we can save quite a bit of computation per pixel by allowing early escapes: As soon as the length of z exceeds 2, we can stop iterating and return white, meaning <em>&#8220;definitely not in the set&#8221;</em>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!x0lH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!x0lH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!x0lH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!x0lH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!x0lH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!x0lH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/684605af-1575-4176-826b-879183491eea_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:75826,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.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_!x0lH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!x0lH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!x0lH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!x0lH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F684605af-1575-4176-826b-879183491eea_2622x1206.png 1456w" sizes="auto, 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>I can start to see something familiar take fractal&#8230; I mean, take shape. I&#8217;m pumped.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h4>6. More and more (and more) iterations </h4><p>This step isn&#8217;t strictly necessary: you can just whack a massive number onto the iteration limit and get it over with, but it&#8217;s also very interesting, so deal with it.</p><p>We can pass arguments into our shader function as well. Arguments that can even be determined by a user&#8230; let&#8217;s adjust our view a little.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">import SwiftUI

struct MandelbrotView: View {
    @State private var iterationLimit = 256.0

    var body: some View {
        Rectangle()
            .fill(ShaderLibrary.mandelbrot(
                .boundingRect,
                .float(Float(iterationLimit))
            ))
            .ignoresSafeArea()
            .overlay(alignment: .topLeading) {
                iterationControls
            }
    }

    private var iterationControls: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Iterations: \(Int(iterationLimit))")
                .font(.headline.monospacedDigit())

            Slider(value: $iterationLimit, in: 1...256, step: 1) {
                Text("Iterations")
            }
            .accessibilityValue(Text("\(Int(iterationLimit))"))
        }
        .padding(16)
        .frame(width: 240)
        .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16))
        .padding(20)
    }
}</code></pre></div><p>I hope I don&#8217;t need to explain sliders to you in SwiftUI. The shader function too gets a fairly simple update.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">[[ stitchable ]]
half4 mandelbrot(float2 position, float4 bounds, float iterationLimit) {
    float2 size = bounds.zw;
    float2 offset = position - bounds.xy - size * 0.5;
    float horizontalSpan = 5.5;
    float unitsPerPoint = horizontalSpan / size.x;
    float2 center = float2(-0.6, 0.0);

    float2 c = center + offset * unitsPerPoint;
    float2 z = float2(0.0, 0.0);

    for (int iteration = 0; iteration &lt; int(iterationLimit); iteration++) {
        z = float2(
            z.x * z.x - z.y * z.y,
            2.0 * z.x * z.y
        ) + c;

        if (length(z) &gt; 2.0) {
            return half4(1.0h, 1.0h, 1.0h, 1.0h);
        }
    }

    return half4(0.0h, 0.0h, 0.0h, 1.0h);
}</code></pre></div><div class="callout-block" data-callout="true"><p>FYI, you aren&#8217;t going crazy: as well as making the iteration limit configurable here, I snuck some very dull viewport geometry into the shader.</p><ul><li><p><strong>center</strong> creates an additional offset to shift the middle of the line, allowing the Mandelbrot shape to fit more neatly. </p></li><li><p><strong>horizontalSpan</strong> determines how many &#8216;units&#8217; of numbers fit horizontally: 5.5 gives us a number line from -3.35 to +2.15 around the center. </p></li><li><p><strong>unitsPerPoint</strong> normalises this width with our view&#8217;s bounds, and tells us how far along the number line each pixel takes us.</p></li></ul><p>Thus each pixel position in our view&#8217;s bounds neatly maps to a starting point for <strong>c</strong> along the complex number plane. </p></div><p>Let&#8217;s play with the slider now.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;b393ea7a-2b30-4c26-b670-67b343692a72&quot;,&quot;duration&quot;:null}"></div><div><hr></div><h4>7. Colouring the escaping pixels</h4><p>We now have something, to speak momentarily like Claude, undeniably <em>Mandelbrot-shaped</em>. But is it Mandelbrot <em>coloured</em>? </p><p>You may be curious: how is it that a set of numbers, either &#8220;in&#8221; or &#8220;not in&#8221;, is always coloured in such crazy ways in those trippy Mandelbrot zoom videos? It&#8217;s because we just need one more rule: colour a number based on how quickly it goes to infinity.</p><blockquote><p><em>Drop the slider. That was mostly illustrative. Let&#8217;s hardcode 256 iterations for now. When we start zooming in (spoiler alert for step #10) we can adjust that as needed.</em></p><p><em>Take .float(Float(iterationLimit)) out of the SwiftUI call to ShaderLibrary.mandelbrot().</em></p></blockquote><p>First let&#8217;s add a function that takes a number of iterations and outputs a half3 representing RGB values from 0 to 1. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">half3 palette(int iterations) {
    float phase = 0.15 * float(iterations);
    return half3(
        0.5 + 0.5 * cos(phase + 4.0),
        0.5 + 0.5 * cos(phase + 2.0),
        0.5 + 0.5 * cos(phase)
    );
}</code></pre></div><p>Long story short, cos(x) is some value from -1 to 1. So by taking 0.5 + 0.5 cos(x), we create a normalised value from 0 to 1 that maps to an RGB value. sin(x) works too, if that&#8217;s more your style.</p><p>Each RGB component is moved a little out of phase with the others <em>(e.g. <strong>cos(phase + 4)</strong>)</em>, so as iterations increase, the return value goes from blueish, to reddish, to greenish, and back again.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!af5C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!af5C!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png 424w, https://substackcdn.com/image/fetch/$s_!af5C!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png 848w, https://substackcdn.com/image/fetch/$s_!af5C!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png 1272w, https://substackcdn.com/image/fetch/$s_!af5C!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!af5C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png" width="1456" height="697" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:697,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:281594,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.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_!af5C!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png 424w, https://substackcdn.com/image/fetch/$s_!af5C!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png 848w, https://substackcdn.com/image/fetch/$s_!af5C!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png 1272w, https://substackcdn.com/image/fetch/$s_!af5C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a9ddabb-7a7a-4052-9a7f-81f1e182192c_2833x1357.png 1456w" sizes="auto, 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>The big win from a trigonometric function is that these keep repeating: the further you zoom into the fine structures at the &#8220;edges&#8221; of the Mandelbrot set, the more iterations something takes to escape to infinity. So the palette function works just the same no matter how far we zoom. </p><p>So the first 1-42 iterations go from blueish, to reddish, to greenish. But then once you zoom down to where it takes, say, 100,000 iterations to escape, the function still works: greenish, to blueish, to reddish. Forever. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">[[ stitchable ]]
half4 mandelbrot(float2 position, float4 bounds) {
    float2 size = bounds.zw;
    float2 offset = position - bounds.xy - size * 0.5;
    float horizontalSpan = 5.5;
    float unitsPerPoint = horizontalSpan / size.x;
    float2 center = float2(-0.6, 0.0);
    float2 c = center + offset * unitsPerPoint;

    float2 z = float2(0.0, 0.0);
    const int maxIterations = 256;
    int iterations = 0;

    for (; iterations &lt; maxIterations; iterations++) {
        z = float2(
            z.x * z.x - z.y * z.y,
            2.0 * z.x * z.y
        ) + c;

        if (length(z) &gt; 2.0) {
            break;
        }
    }

    if (iterations == maxIterations) {
        return half4(0.0h, 0.0h, 0.0h, 1.0h);
    }

    return half4(palette(iterations), 1.0h);
}</code></pre></div><p>Now we&#8217;ve locked the max iteration count to 256 to get a fine structure we can begin to zoom into. Here, we still break out early if we start to escape, but instead of painting pure white, we apply our new palette.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rRhm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rRhm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!rRhm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!rRhm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!rRhm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!rRhm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:430100,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.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_!rRhm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!rRhm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!rRhm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!rRhm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F931a5a06-dd17-42d3-bed8-49f4d8d773fd_2622x1206.png 1456w" sizes="auto, 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>It&#8217;s&#8230; it&#8217;s beautiful. </p><p>Already, we can see the mathematics taking shape&#8230; quite literally, I guess.</p><p>Oh, also, as long as you get oscillation across each colour component, the numbers in the palette function are kind of arbitrary:</p><ul><li><p>We can tune the <strong>phase</strong> multiplier to get &#8216;faster&#8217; colour oscillation, </p></li><li><p>We can change the constant added in the cos function to find a different colour palette. </p></li></ul><p>I prefer to space these out evenly, using multiples of <strong>M_PI_F</strong>. To get the neatest spacing, and therefore the most vibrant colour palette, I space the colours by <strong>2&#960;/3</strong>. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">half3 palette(int iterations) {
    float phase = 0.30 * float(iterations);
    return half3(
        0.5 + 0.5 * cos(phase + 2.0 * M_PI_F / 3.0),
        0.5 + 0.5 * cos(phase),
        0.5 + 0.5 * cos(phase + 4.0 * M_PI_F / 3.0)
    );
}</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1-f6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1-f6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!1-f6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!1-f6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!1-f6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!1-f6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:436475,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.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_!1-f6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!1-f6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!1-f6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!1-f6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8dce4e-2c26-425e-8c1b-e271ec367382_2622x1206.png 1456w" sizes="auto, 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>You know what, everyone always gives blue outsides to their &#8216;brots. </p><p>This looks way better. Let&#8217;s keep it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h4>8. Giving SwiftUI a camera</h4><p>What do we actually have left to do? We&#8217;ve got the entire mathematical formulation built, we just need to add the ability to 1) move around the fractal, and 2) move <em>into</em> the shape. Conceptually, this is effectively <a href="https://blog.jacobstechtavern.com/i/175056399/camera">adding a videogame camera</a> to the app, storing both a location and a zoom level.</p><p>The complicated shader work is over. The rest of our code doesn&#8217;t even touch the iteration loop or pixel colouring logic: in fact, everything else we&#8217;re looking at just affects the initial value of <strong>c</strong>.</p><div class="callout-block" data-callout="true"><p>&#9888;&#65039; Warning: this particular step is super boring. </p></div><p>To start building our camera, we just need to store the <strong>centre</strong> property and pass it to our shader. This allows us to map the viewport centre to a complex coordinate. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">struct MandelbrotView: View {
    let center = CGPoint(x: -0.6, y: 0.0)

    var body: some View {
        Rectangle()
            .fill(ShaderLibrary.mandelbrot(
                .boundingRect,
                .float2(center)
            ))
            .ignoresSafeArea()
    }
}</code></pre></div><p>This property is added to our Mandelbrot shader&#8217;s arguments, and used as the starting point for computing the initial value of <strong>c</strong>, that is, the complex number associated with a specific pixel position.  </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">[[ stitchable ]]
half4 mandelbrot(
    float2 position,
    float4 bounds,
    float2 center
) {
    float2 size = bounds.zw;
    float2 offset = position - bounds.xy - size * 0.5;

    float horizontalSpan = 5.5;
    float unitsPerPoint = horizontalSpan / size.x;
    float2 c = center + offset * unitsPerPoint;

    float2 z = float2(0.0, 0.0);
    const int maxIterations = 256;
    int iterations = 0;

    for (; iterations &lt; maxIterations; iterations++) {
        z = float2(
            z.x * z.x - z.y * z.y,
            2.0 * z.x * z.y
        ) + c;

        if (length(z) &gt; 2.0) {
            break;
        }
    }

    if (iterations == maxIterations) {
        return half4(0.0h, 0.0h, 0.0h, 1.0h);
    }

    return half4(palette(iterations), 1.0h);
}</code></pre></div><p>I&#8217;ll save you scrolling up, we&#8217;ve done something really simple: we&#8217;ve taken this one line out of the shader, and passed it through SwiftUI instead.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:&quot;f5b86a71-39c0-4641-bb2b-8d5bcd71194f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">float2 center = float2(-0.6, 0.0);</code></pre></div><p>And, the result is&#8230; nothing different from before. Don&#8217;t say I didn&#8217;t warn you.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jxpX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jxpX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!jxpX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!jxpX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!jxpX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!jxpX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/afaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:436475,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.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_!jxpX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!jxpX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!jxpX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!jxpX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafaf310b-28aa-46f0-b9cf-951708824df9_2622x1206.png 1456w" sizes="auto, 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><h4>9. Dragging around the screen</h4><p>We&#8217;re nearly there. One more until the big one. We just need to make the centre property mutable in SwiftUI by implementing a drag gesture. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">struct MandelbrotView: View {
    @State private var center = CGPoint(x: -0.6, y: 0.0)
    @State private var currentCenter = CGPoint(x: -0.6, y: 0.0)
    @State private var viewWidth: CGFloat = 1000

    var body: some View {
        Rectangle()
            .fill(ShaderLibrary.mandelbrot(
                .boundingRect,
                .float2(center)
            ))
            .onGeometryChange(for: CGFloat.self, of: \.size.width) {
                viewWidth = $0
            }
            .gesture(
                DragGesture()
                    .onChanged { value in
                        let unitsPerPoint = 5.5 / viewWidth
                        center = CGPoint(
                            x: currentCenter.x - value.translation.width * unitsPerPoint,
                            y: currentCenter.y - value.translation.height * unitsPerPoint
                        )
                    }
                    .onEnded { _ in
                        currentCenter = center
                    }
            )
            .ignoresSafeArea()
    }
}</code></pre></div><p>Hopefully nothing new here: we add a <strong>gesture</strong>, and update the <strong>centre</strong> property passed into the shader as it changes by taking away the (scaled) translation from the original center value. </p><p>We&#8217;re scaling the translation by dividing the <strong>horizontalSpan</strong> of 5.5 from before <em>(the number line from -3.35 to 2.15)</em>, by the number of screen points, so we can scale the screen&#8217;s points to &#8220;units&#8221; on the number line.</p><p>I computed <strong>viewWidth</strong>, the number of points along the screen, using <strong>onGeometryChange</strong> because GeometryReader is a cesspit of snakes which I try to avoid at all costs &#128013;.</p><p>Now we can start to dynamically shift the camera about with a finger.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;d56efe9d-378a-4ed8-a15b-18278be78d40&quot;,&quot;duration&quot;:null}"></div><p>Ok, you ready for the big finale? Things are about to get bonkers. </p><div><hr></div><h4>10. The Mandelbrot Zoom&#8482;</h4><p>I saved the best to last. This is what a Mandelbrot app is all about. Victory lap. Cheers for sticking with it. To magnify our fractal, we need to create a pinch-to-zoom gesture, and pass the zoom level to our shader.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">struct MandelbrotView: View {
    @State private var center = CGPoint(x: -0.6, y: 0.0)
    @State private var currentCenter = CGPoint(x: -0.6, y: 0.0)
    @State private var viewWidth: CGFloat = 1000
    @State private var horizontalSpan: CGFloat = 5.5
    @State private var currentSpan: CGFloat = 5.5

    var body: some View {
        Rectangle()
            .fill(ShaderLibrary.mandelbrot(
                .boundingRect,
                .float2(center),
                .float(Float(horizontalSpan))
            ))
            .onGeometryChange(for: CGFloat.self, of: \.size.width) {
                viewWidth = $0
            }
            .gesture(
                DragGesture()
                    .onChanged { value in
                        let unitsPerPoint = horizontalSpan / viewWidth
                        center = CGPoint(
                            x: currentCenter.x - value.translation.width * unitsPerPoint,
                            y: currentCenter.y - value.translation.height * unitsPerPoint
                        )
                    }
                    .onEnded { _ in
                        currentCenter = center
                    }
            )
            .simultaneousGesture(
                MagnifyGesture()
                    .onChanged { value in
                        horizontalSpan = min(max(currentSpan / value.magnification, 0.000_01), 5.5)
                    }
                    .onEnded { _ in
                        currentSpan = horizontalSpan
                    }
            )
            .ignoresSafeArea()
    }
}</code></pre></div><p>Since the gesture is interactive, we need to store both a <strong>currentSpan</strong> to store the &#8220;original&#8221; value and <strong>horizontalSpan</strong> to track the &#8220;live&#8221; value during a pinch gesture, overwriting the original value <strong>onEnded</strong>. You can also handle this with <a href="https://developer.apple.com/documentation/swiftui/gesturestate">@GestureState</a>, but the code ended up way harder to follow. Our centre-point computation in the DragGesture now uses this dynamic span from magnification to calculate <strong>unitsPerPoint</strong>. </p><p>To implement the pinch gesture, we use the run-of-the-mill <strong><a href="https://developer.apple.com/documentation/swiftui/view/simultaneousgesture(_:including:)">simultaneousGesture</a></strong> API to add an additional gesture to the view, <strong><a href="https://developer.apple.com/documentation/swiftui/magnifygesture">MagnifyGesture</a>,</strong> which tracks a magnification multiplier interactively. A magnification of 2 halves the visible <strong>horizontalSpan</strong>, zooming into the fractal.</p><p>This <strong>horizontalSpan</strong> property represents the length across the number line traversed by our camera&#8217;s zoom level. Again, none of the maths changes. All we&#8217;re doing is extracting our hardcoded <strong>horizontalSpan</strong> from before and making it an argument to our shader. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">[[ stitchable ]]
half4 mandelbrot(
    float2 position,
    float4 bounds,
    float2 center,
    float horizontalSpan
) {
    float2 size = bounds.zw;
    float2 offset = position - bounds.xy - size * 0.5;

    float unitsPerPoint = horizontalSpan / size.x;
    float2 c = center + offset * unitsPerPoint;

    float2 z = float2(0.0, 0.0);
    const int maxIterations = 256;
    int iterations = 0;

    for (; iterations &lt; maxIterations; iterations++) {
        z = float2(
            z.x * z.x - z.y * z.y,
            2.0 * z.x * z.y
        ) + c;

        if (length(z) &gt; 2.0) {
            break;
        }
    }

    if (iterations == maxIterations) {
        return half4(0.0h, 0.0h, 0.0h, 1.0h);
    }

    return half4(palette(iterations), 1.0h);
}</code></pre></div><p>We pin the <strong>horizontalSpan</strong> between 5.5 units (at max zoom-out) and 0.00001 (for max magnification). You&#8217;ll see why in a moment.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vOqN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vOqN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!vOqN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!vOqN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!vOqN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!vOqN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:502898,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.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_!vOqN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!vOqN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!vOqN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!vOqN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc4f68c9-015c-4a28-99c4-30cae9bb6a5d_2868x1320.png 1456w" sizes="auto, 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="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pSD_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pSD_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!pSD_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!pSD_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!pSD_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!pSD_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:864402,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.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_!pSD_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!pSD_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!pSD_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!pSD_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb325f682-917e-4f3b-b7b2-6b073286f07a_2868x1320.png 1456w" sizes="auto, 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="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zbQX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zbQX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!zbQX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!zbQX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!zbQX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!zbQX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1377423,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.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_!zbQX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!zbQX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!zbQX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!zbQX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f80bb98-7cde-4093-a3fd-3757e5a58b4b_2868x1320.png 1456w" sizes="auto, 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="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hCFM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hCFM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!hCFM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!hCFM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!hCFM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!hCFM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/156fdf68-b74f-4291-a631-46397469b628_2868x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2439808,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.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_!hCFM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!hCFM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!hCFM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!hCFM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F156fdf68-b74f-4291-a631-46397469b628_2868x1320.png 1456w" sizes="auto, 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="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ppsy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ppsy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!Ppsy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!Ppsy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!Ppsy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ppsy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3413840,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.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_!Ppsy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!Ppsy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!Ppsy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!Ppsy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60bee9f2-6fec-4e22-9792-4fe1fdc947b0_2868x1320.png 1456w" sizes="auto, 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="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!00Bt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!00Bt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!00Bt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!00Bt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!00Bt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!00Bt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1720080,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.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_!00Bt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!00Bt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!00Bt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!00Bt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1df11fa-e61b-448e-89e1-2480dccb6287_2868x1320.png 1456w" sizes="auto, 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="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UG7X!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UG7X!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!UG7X!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!UG7X!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!UG7X!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!UG7X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1306032,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.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_!UG7X!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!UG7X!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!UG7X!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!UG7X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129fb7ac-12a8-447b-931a-3b0328a47d34_2868x1320.png 1456w" sizes="auto, 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>Totally mesmerising, until&#8230; I guess until it isn&#8217;t. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aJcB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aJcB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!aJcB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!aJcB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!aJcB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!aJcB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:159768,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.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_!aJcB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png 424w, https://substackcdn.com/image/fetch/$s_!aJcB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png 848w, https://substackcdn.com/image/fetch/$s_!aJcB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png 1272w, https://substackcdn.com/image/fetch/$s_!aJcB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff679430c-e4a7-4d1d-9a41-fdcaa26fef47_2868x1320.png 1456w" sizes="auto, 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 class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2><strong>The end of the line</strong> </h2><p>That minimum horizontalSpan across our number line is 0.00001 because, at this point, we run out of precision when performing recursive complex analysis with <strong>float32</strong>. </p><p>Once we zoom in far enough, with a tiny <strong>horizontalSpan</strong>, neighbouring pixels at this precision round out to the <em>same </em>complex number, giving us pixellated rectangular blocks at this limit.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!t06b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t06b!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!t06b!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!t06b!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!t06b!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!t06b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:185731,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.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_!t06b!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!t06b!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!t06b!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!t06b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a873504-e7ae-4c00-9e0a-a02f0d5ba370_2622x1206.png 1456w" sizes="auto, 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>This is the end of the line for the neat, code-golfed, 90-lines-total app we set up today. But for those willing to ask nicely for thousands more lines of code, we can try out some more computationally elaborate solutions. </p><p>There&#8217;s a mathematical trick called <a href="https://www.ultrafractal.com/help/index.html?/help/formulas/perturbationcalculations.html">perturbation</a> which allows us to zoom to arbitrary levels of precision without melting the GPU. </p><p>We can use representations other than float32 <em>(Metal doesn&#8217;t have a float64, and if it did, that just kicks the can down the road)</em>. We instead store complex numbers as <em>chunks</em> using an array of numbers, allowing for <strong>arbitrary</strong> precision, and hand-roll* the resulting complex square maths. As we go deeper, we compute one ultra-high-precision &#8220;reference orbit&#8221; in the centre, and calculate other pixels on-screen as tiny deviations from this reference, which can be done drastically faster.</p><blockquote><p><em>*&#8220;hand&#8221;-roll</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TXn_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TXn_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!TXn_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!TXn_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!TXn_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!TXn_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:779421,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.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_!TXn_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!TXn_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!TXn_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!TXn_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4aa050e-62b9-4d3b-8e85-e50756aa8a53_2622x1206.png 1456w" sizes="auto, 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><h2>I almost got through a whole article without mentioning UIKit </h2><p>Naturally, I thought to myself &#8220;can UIKit can help polish our app?&#8221;. </p><p>We can actually replace the SwiftUI gestures with <a href="https://blog.jacobstechtavern.com/p/real-engineers-use-metal">a UIScrollView</a> to handle gestures like 1-finger pan, 2-finger pinch, the deceleration physics you know and love, as well as add a million free, invisible, optimisations like &#8220;protecting against accidental flings when a pinch ends&#8221; and &#8220;stopping an accidental pan when one finger in a pinch gesture lifts up first&#8221;.</p><p>Now, it was time to unplug my maths brain and plug in my product brain, and implement a number of neat quality-of-life features.</p><ul><li><p>The biggest win was separating gestures from drawing: this meant the main thread kept free to handle panning and zooming around the fractal, with perfectly smooth drawing on the GPU. Pan gestures translate the whole image we&#8217;ve drawn, and pinch gestures zoom into the existing painted pixels, rather than forcing each single pixel to re-compute immediately when the UIScrollView moves.</p></li><li><p>Because the maths slows down the deeper we go, I computed pixels middle-out so you can keep zooming before all the maths finishes.</p></li><li><p>I added pre-installed reference zooms, the ability to save and auto-zoom to new features of the fractal like the <em>Elephant Valley</em> or <em>Seahorse Valley</em>, and <strong>lots</strong> of colour palettes <em>(some with new post-processing shaders on top of the view itself)</em></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lBUL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lBUL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png 424w, https://substackcdn.com/image/fetch/$s_!lBUL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png 848w, https://substackcdn.com/image/fetch/$s_!lBUL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png 1272w, https://substackcdn.com/image/fetch/$s_!lBUL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!lBUL!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png" width="1200" height="599.1758241758242" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:727,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:6136686,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lBUL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png 424w, https://substackcdn.com/image/fetch/$s_!lBUL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png 848w, https://substackcdn.com/image/fetch/$s_!lBUL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png 1272w, https://substackcdn.com/image/fetch/$s_!lBUL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25447c24-1acc-422b-ae93-fcd16f29f332_3300x1647.png 1456w" sizes="auto, 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">There are more on the store version. Water is 100% the coolest, due to the distortion shader and dynamic lighting post-processing.</figcaption></figure></div><ul><li><p>I implemented automatic zooming, so a tap anywhere will automatically zoom towards a random edge in that direction, forever. This was huge because it made it possible for my kids to play with it. It worked with a tiny GPU readback to locate boundary details.</p></li><li><p>This caused a lot of jitter between computed frames, so I set up a dynamic algorithm to learn, through each iteration, how quickly your GPU was processing everything, allowing for a smooth, auto-adjusting zoom velocity.</p></li><li><p>I implemented caching for pre-installed and saved zooms, so a lot of the heavy maths was ready up-front, making subsequent zooms quicker. It took a bit of experimenting before I relegated work like GPU readbacks, compression, and disk writes to a background job to avoid it eating drawing cycles while a user was moving around.</p></li><li><p>At some deep zooms, the fractal turned into noise because <em>adjacent pixels</em> had drastically different numbers of iterations before escaping. Instead of hardcoding integer iteration counts, I used the <strong>magnitude of z when it escaped</strong> as well as the integer iteration count, to compute much smoother gradations. I also applied a log to the number of iterations at deeper zooms to smooth out the speed of colour changes. See before this fix (top) and after (bottom).</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i_Ca!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i_Ca!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png 424w, https://substackcdn.com/image/fetch/$s_!i_Ca!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png 848w, https://substackcdn.com/image/fetch/$s_!i_Ca!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png 1272w, https://substackcdn.com/image/fetch/$s_!i_Ca!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!i_Ca!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png" width="1456" height="1455" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1455,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6267826,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.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_!i_Ca!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png 424w, https://substackcdn.com/image/fetch/$s_!i_Ca!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png 848w, https://substackcdn.com/image/fetch/$s_!i_Ca!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png 1272w, https://substackcdn.com/image/fetch/$s_!i_Ca!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffceb10ca-575a-487c-9341-563bb3574ef8_2000x1998.png 1456w" sizes="auto, 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><blockquote><p><em>You can actually apply this to the code-golfed version above with a 2-line change: adjust the shader to accept <strong>float iterations</strong> in palette() and apply logarithms to the argument.</em> </p></blockquote><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;cpp&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-cpp">half3 palette(float iterations) {
    float phase = 0.30 * iterations;
    return half3(
        0.5 + 0.5 * cos(phase + 2.0 * M_PI_F / 3.0),
        0.5 + 0.5 * cos(phase),
        0.5 + 0.5 * cos(phase + 4.0 * M_PI_F / 3.0)
    );
}

[[ stitchable ]]
half4 mandelbrot(
    float2 position,
    float4 bounds,
    float2 center,
    float horizontalSpan
) {
    float2 size = bounds.zw;
    float2 offset = position - bounds.xy - size * 0.5;

    float unitsPerPoint = horizontalSpan / size.x;
    float2 c = center + offset * unitsPerPoint;

    float2 z = float2(0.0, 0.0);
    const int maxIterations = 256;
    int iterations = 0;

    for (; iterations &lt; maxIterations; iterations++) {
        z = float2(
            z.x * z.x - z.y * z.y,
            2.0 * z.x * z.y
        ) + c;

        if (length(z) &gt; 2.0) {
            break;
        }
    }

    if (iterations == maxIterations) {
        return half4(0.0h, 0.0h, 0.0h, 1.0h);
    }

    return half4(palette(iterations + 1 - log2(log2(length(z)))), 1.0h);
}</code></pre></div><ul><li><p>Having fully-loaded Fable 5.1 and GPT-Astra-6.0 subscriptions helped me out tremendously. I could trivially request research into countless open-source Mandelbrot implementations to find maths optimisations I missed, perform adversarial reviews of my existing work, and study existing apps on the store to locate any features I forgot <em>(such as pinch to rotate, whoops)</em>.</p></li><li><p>I noticed that some store apps listed 10&#178;&#8304;&#8304;&#8304;x zooms. If you&#8217;re doing the maths right, you can obviously go to arbitrary, infinite, levels of precision, but I stupidly only listed 10&#185;&#179;&#8312;x. Not good enough! So, naturally, I researched more deep points-of-interest and now I market myself as 10&#185;&#8304;&#8304;&#8304;&#8304;x (or 10&#185;&#8304;&#7503;). </p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QJuB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QJuB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!QJuB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!QJuB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!QJuB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!QJuB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png" width="1456" height="670" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:670,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7199884,&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://blog.jacobstechtavern.com/i/171250506?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.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_!QJuB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png 424w, https://substackcdn.com/image/fetch/$s_!QJuB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png 848w, https://substackcdn.com/image/fetch/$s_!QJuB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png 1272w, https://substackcdn.com/image/fetch/$s_!QJuB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52880aa5-dea9-48d2-b690-aee06f797e86_2622x1206.png 1456w" sizes="auto, 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><ul><li><p>Finally, the auto-zoom into random edges and previously-saved areas still felt a little janky to me, even with the smoothing algorithm. All our main-thread-saving optimisations with UIKit gestures and preventing redraws on the GPU made manual pinch-to-zoom feel much more performant. So I re-worked the auto-zooming implementation, kept the caching, but forced it to behave like the gesture-based manual zooming. And <strong>boy</strong> is it fast now.</p></li></ul><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;d80269b7-c40d-4865-bd23-51fccff95e55&quot;,&quot;duration&quot;:null}"></div><p>Alright, I reckon we&#8217;re done here. Give it a look!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://apps.apple.com/us/app/ultra-mandelbrot-fractal-zoom/id6811671572&quot;,&quot;text&quot;:&quot;Download \&quot;Ultra Mandelbrot\&quot; here&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://apps.apple.com/us/app/ultra-mandelbrot-fractal-zoom/id6811671572"><span>Download "Ultra Mandelbrot" here</span></a></p><div><hr></div><h2>Last Orders &#127866;</h2><p>This article has been on my agenda for <em>years</em>. I&#8217;ve got that special flavour of autism that makes the Mandelbrot set catnip to me. What do you mean that <em>nobody in history has ever seen this edge before</em>? There&#8217;s a certain romanticism to it. </p><p>The Mandelbrot set is the coolest thing in mathematics because it creates infinite complexity from an extremely simple rule: square and add c, recursively. </p><p>I hope you&#8217;ve enjoyed our romp through Metal shaders, from the basics of painting a pixel on your SwiftUI, to our initial Mandelbrot computation, to applying camera semantics to introduce pan and zoom gestures to explore the fractal.</p><p>I may or may not have gone a little overboard with the subsequent optimisations over the last few weekends, the code from which I will not darken the daylight by sharing. But you&#8217;re welcome to download the app!    </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://apps.apple.com/us/app/ultra-mandelbrot-fractal-zoom/id6811671572&quot;,&quot;text&quot;:&quot;Download \&quot;Ultra Mandelbrot\&quot; here&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://apps.apple.com/us/app/ultra-mandelbrot-fractal-zoom/id6811671572"><span>Download "Ultra Mandelbrot" here</span></a></p><p><em>(The name Metal Mandelbrot was taken &#129394;)</em></p><div><hr></div><h5>Sponsored Link</h5><blockquote><h4><a href="https://hubs.la/Q04x_hNj0">Is your mobile app under attack right now?</a></h4><p>Discover why real-time threat monitoring is crucial to your cybersecurity posture through a simulated attack demo. </p><p>Join GuardSquare on Oct 20th to uncover why threat monitoring is essential to the complete, end-to-end protection of your mobile app. </p><p>Sign up <a href="https://hubs.la/Q04x_hNj0">here</a>.</p></blockquote>]]></content:encoded></item><item><title><![CDATA[NFC, BLE, UWB, WTF?!]]></title><description><![CDATA[How iOS talks to your hardware]]></description><link>https://blog.jacobstechtavern.com/p/nfc-ble-uwb</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/nfc-ble-uwb</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 30 Sep 2026 15:02:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!i2i9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i2i9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i2i9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!i2i9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!i2i9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!i2i9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i2i9!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:3997692,&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://blog.jacobstechtavern.com/i/213251496?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i2i9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!i2i9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!i2i9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!i2i9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1bfc43d6-3af5-4a15-820a-63f0656493c2_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>I recently got a new car, for the first time in my life. I won&#8217;t tell you which, because I&#8217;ll be cancelled. But it felt like a spaceship compared to my second-hand 2017 Toyota Aygo: a car whose design was frozen in 2013, that was manufactured in 2016, and purchased by me in 2019.</p><p>Inconveniences I took as a fact of life, like manually connecting my phone via Bluetooth each time, evaporated. Incredibly, I no longer even have to pause and unpause my music, over and over, to get the audio session to transfer over to the car speaker.</p><p>But Bluetooth is <em>sooo</em> 1997. Literally. Or, arguably, <a href="https://en.wikipedia.org/wiki/Harald_Bluetooth">perhaps it&#8217;s </a><em><a href="https://en.wikipedia.org/wiki/Harald_Bluetooth">sooo</a></em><a href="https://en.wikipedia.org/wiki/Harald_Bluetooth"> 958</a>. </p><p>This is no longer the only game in town when it comes to getting hardware to chat to each other. My shiny new car <em>(that I totally did not scratch on day 2 of owning it, please don&#8217;t look at the thumbnail too closely)</em> actually uses a wide range of modern wireless communication tech, such as:</p><ul><li><p>NFC (near-field communication) </p></li><li><p>BLE (bluetooth low energy)</p></li><li><p>UWB (ultra-wideband)</p></li></ul><p>Some of these are protocols, some are specific radio standards, some require custom physical hardware work. But each 3-letter initialism is essentially a marketing term for an umbrella of <strong>wireless technologies</strong> that work at different layers.</p><p>Ok. So. Today&#8230;</p><ul><li><p>I&#8217;ll demystify <em>(I miss when we were still allowed to say that) </em>these technologies so you get the gist of how they work.</p></li><li><p>With some nifty hardware I procured, I&#8217;ll give you a live demo to show what these technologies are useful for.</p></li><li><p>I&#8217;ll show real, working code that allows you to interact with these technologies in your apps today. </p></li></ul><div><hr></div><h5>Sponsored Link</h5><blockquote><h4><strong><a href="https://www.runway.team/features/triage?utm_source=jacobs-tech-tavern&amp;utm_medium=newsletter&amp;utm_campaign=triage-launch&amp;utm_content=26-09-30">Let agents pick up mobile issues proactively</a></strong></h4><p>Your agents are getting good at fixing things, but humans still have to monitor, review, and assign issues first. Triage by Runway pulls every mobile issue into one inbox: test results, beta feedback, reviews, crashes, and more. Then auto-assign, or let agents connect via MCP to pick up fixes. Read more <a href="https://www.runway.team/features/triage?utm_source=jacobs-tech-tavern&amp;utm_medium=newsletter&amp;utm_campaign=triage-launch&amp;utm_content=26-09-30">here</a>.</p></blockquote><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_!ji2O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ji2O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic 424w, https://substackcdn.com/image/fetch/$s_!ji2O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic 848w, https://substackcdn.com/image/fetch/$s_!ji2O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic 1272w, https://substackcdn.com/image/fetch/$s_!ji2O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ji2O!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic" width="1200" height="900" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;IMG_7839.HEIC&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;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="IMG_7839.HEIC" title="IMG_7839.HEIC" srcset="https://substackcdn.com/image/fetch/$s_!ji2O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic 424w, https://substackcdn.com/image/fetch/$s_!ji2O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic 848w, https://substackcdn.com/image/fetch/$s_!ji2O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic 1272w, https://substackcdn.com/image/fetch/$s_!ji2O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F963c30eb-fb65-4d54-b5cf-105ea480f169_5712x4284.heic 1456w" sizes="auto, 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">I didn&#8217;t want to shell out &#163;69 for a dedicated UWB chip when I could just use this discarded iPhone 13 mini, unsubscribe right now if you&#8217;ve got a problem with that. </figcaption></figure></div><p>Once we&#8217;ve mastered short-range wireless communication technology, I&#8217;m going to <strong>put all these pieces of hardware together</strong> to implement the player-to-player communication system from Dark Souls and Elden Ring&#8230; in real life.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xfgm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xfgm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp 424w, https://substackcdn.com/image/fetch/$s_!Xfgm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp 848w, https://substackcdn.com/image/fetch/$s_!Xfgm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp 1272w, https://substackcdn.com/image/fetch/$s_!Xfgm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xfgm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp" width="1080" height="810" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:810,&quot;width&quot;:1080,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;so-first-time-playing-i-read-this-message-and-didnt-think-v0-x1Bm1vsHVrO6-q4t2C8_Zr08azNpj7VFMDqOf1dqkEE.jpeg.webp&quot;,&quot;title&quot;:&quot;so-first-time-playing-i-read-this-message-and-didnt-think-v0-x1Bm1vsHVrO6-q4t2C8_Zr08azNpj7VFMDqOf1dqkEE.jpeg.webp&quot;,&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="so-first-time-playing-i-read-this-message-and-didnt-think-v0-x1Bm1vsHVrO6-q4t2C8_Zr08azNpj7VFMDqOf1dqkEE.jpeg.webp" title="so-first-time-playing-i-read-this-message-and-didnt-think-v0-x1Bm1vsHVrO6-q4t2C8_Zr08azNpj7VFMDqOf1dqkEE.jpeg.webp" srcset="https://substackcdn.com/image/fetch/$s_!Xfgm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp 424w, https://substackcdn.com/image/fetch/$s_!Xfgm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp 848w, https://substackcdn.com/image/fetch/$s_!Xfgm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp 1272w, https://substackcdn.com/image/fetch/$s_!Xfgm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc2cacc1c-2b3b-4b74-baa8-f7b02ca643cd_1080x810.webp 1456w" sizes="auto, 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>This involves writing hidden messages on the ground that users can search for, unveil, and read to get a handy tip (or perhaps a not-so-handy one). Users can then leave their own messages for the next players!</p><div><hr></div><h2>Contents</h2><ul><li><p>NFC</p></li><li><p>Implementing NFC in your app</p></li><li><p>BLE</p></li><li><p>Implementing BLE in your app</p></li><li><p>UWB</p></li><li><p>Implementing UWB in your app</p></li><li><p>Putting it all together </p></li><li><p>Last Orders</p></li></ul><div><hr></div><h1>NFC</h1><p>NFC, or <strong>near-field communication</strong>, is a family of short-range wireless standards that operate at 13.56MHz radio frequencies. It&#8217;s a specific branch of <em>radio frequency identification</em>, or RFID. You use it every day you pay with contactless, Apple Pay, <a href="https://developer.apple.com/documentation/appclip/creating-app-clip-codes">or even download an App Clip</a>! </p><p>NFC is very useful for reading and writing low-bandwidth information at centimetre-scale, i.e. <em>deliberate</em>, range. </p><p>But NFC doesn&#8217;t just use the device radio. And it works even if one partner is un-powered. It does something genius, using Faraday&#8217;s law of induction:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\mathcal{E} = -N \\frac{d\\Phi_B}{dt}&quot;,&quot;id&quot;:&quot;KRJJDCONAM&quot;}" data-component-name="LatexBlockToDOM"></div><p>Get it? </p><p>Right, for those that don&#8217;t have their second-year electromagnetism course fresh in their mind, this law basically shows that you can generate an electric current, at a distance, by moving a magnetic field through a coil. It&#8217;s only how the global power grid works.</p><p>In an NFC interaction, the powered reader/writer produces a changing magnetic field that produces a voltage across a wire coil in the passive NFC tag. This drives a current and powers the chip, allowing it to accept a signal and read/write data.</p><p>This magnetic field, as well as oscillating at the 13.56MHz NFC frequency, also encodes information in its amplitude, so powering and writing happen simultaneously!  </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rSWn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rSWn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png 424w, https://substackcdn.com/image/fetch/$s_!rSWn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png 848w, https://substackcdn.com/image/fetch/$s_!rSWn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png 1272w, https://substackcdn.com/image/fetch/$s_!rSWn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!rSWn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png" width="500" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:178756,&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://blog.jacobstechtavern.com/i/213251496?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.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_!rSWn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png 424w, https://substackcdn.com/image/fetch/$s_!rSWn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png 848w, https://substackcdn.com/image/fetch/$s_!rSWn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png 1272w, https://substackcdn.com/image/fetch/$s_!rSWn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3ec8dea-a5dc-41cc-a246-9d792b9be8f6_500x276.png 1456w" sizes="auto, 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">An oldie but a goldie.</figcaption></figure></div><p>Now let&#8217;s take a look at how to implement NFC in your own apps using CoreNFC.</p><div class="callout-block" data-callout="true"><p>This article is exclusive to paid members. Paid members learn how to implement CoreNFC, as well as Core Bluetooth (BLE) and Nearby Interaction (UWB).</p><p>You also get:</p><p>&#9875;&#65039; Access my <a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 50 paywalled articles</a> (including this one)<br>&#128640; Read <a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a> a month before anyone else<br>&#129525; Master concurrency with my <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a> and <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br>&#129489;&#8205;&#128640; Get a free copy of my new eBook, &#8220;<a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a>&#8221;<br>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</p><p>Redeem your 2-week free trial today:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=213251496&quot;,&quot;text&quot;:&quot;Get 14 day free trial&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=213251496"><span>Get 14 day free trial</span></a></p><p><em>If you don&#8217;t fancy it, here&#8217;s a free article that sort of hits the same notes:</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;d447bb35-72ae-4e0d-884c-c277708e4d9f&quot;,&quot;caption&quot;:&quot;One of the reasons I reckon I&#8217;m a decent writer is that I can remember what it&#8217;s like to be very junior. Noob-y mistakes were my bread and butter. I hailed from the &#8220;no question is a stupid question&#8221; school of thought, and had zero filter when I felt blocked.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;URLSession to Electrons: How Networking works on iOS&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Join 10,000 engineers learning advanced concurrency, agentic iOS engineering, and Swift performance for 10 minutes a week.&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2026-04-28T15:03:29.486Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!OyKR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ae2a288-86f6-4fe7-b09a-b7e3d6326a98_1680x1200.png&quot;,&quot;cover_image_alt&quot;:null,&quot;cover_image_crop&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/urlsession-to-electrons&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:180131196,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:29,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div></div>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/nfc-ble-uwb">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Who needs AI psychosis?! I gave myself VR psychosis 🤠]]></title><description><![CDATA[Shipping production videogames from the pub]]></description><link>https://blog.jacobstechtavern.com/p/vr-psychosis</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/vr-psychosis</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 23 Sep 2026 15:01:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!IWJV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IWJV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IWJV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png 424w, https://substackcdn.com/image/fetch/$s_!IWJV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png 848w, https://substackcdn.com/image/fetch/$s_!IWJV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png 1272w, https://substackcdn.com/image/fetch/$s_!IWJV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IWJV!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:3598876,&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://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IWJV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png 424w, https://substackcdn.com/image/fetch/$s_!IWJV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png 848w, https://substackcdn.com/image/fetch/$s_!IWJV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png 1272w, https://substackcdn.com/image/fetch/$s_!IWJV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cdcaeb5-4a99-4178-a605-de5b1aa16aa9_1790x1278.png 1456w" sizes="100vw" fetchpriority="high"></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>Modern life is an impossible luxury. You have a king&#8217;s ransom of salt and spices in your home, clumpy from neglect. These were once <a href="https://artsandculture.google.com/story/spices-a-luxury-product-real-academia-de-gastronomia-espa%C3%B1ola/OQXxIzw5DtXFKg?hl=en">worth their weight in gold</a>, and abundance has relegated them to the slithy toves of ALDI. It took <strong>centuries</strong> of globalisation to transform spice into an everyday commodity.</p><p>In the early 2000s, I read a tabloid profile of a lottery winner: <em>&#8220;I might buy my son a video game&#8221;</em>. The journalist guffawed, <em>&#8220;you know you could pay a company to build him his own game!&#8221;</em> A hilarious extravagance. And yet in the last ~year, something you can just <em>do</em>, the price collapsing from $millions down to $20/month.</p><p>AI Psychosis has never been so cheap.</p><p>When you&#8217;re set up properly&#8230; you can do some pretty bonkers stuff. I&#8217;ve become obsessed with vibe coding VR games, auto deploying them to the web, and playtesting to iterate. In short, I want to share my descent into VR psychosis with you.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;052c25b2-528d-4797-9b2b-fdf82da3d974&quot;,&quot;duration&quot;:null}"></div><p>Can&#8217;t read? Just want to play some VR games? I got you covered:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://games.jacobstechtavern.com&quot;,&quot;text&quot;:&quot;Play my VR games here&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://games.jacobstechtavern.com"><span>Play my VR games here</span></a></p><p>With a game, you know within 10 seconds if you&#8217;re into it. A compelling story and overarching goal are great: rescue the princess. But even more critical is an engaging <strong>primary gameplay loop</strong>: jump on a <em>lot</em> of Goombas along the way. </p><p>Iterative game generation is the fastest way I&#8217;ve found to hone <em>taste</em>. In a new world underpinned by agentic engineering, it&#8217;s the most defensible skill of all.</p><p>Here&#8217;s what you might get out of this <em>(somewhat meandering)</em> life story:</p><ul><li><p>How I ended up regularly shipping VR games from the pub.</p></li><li><p>How to set up an auto-deploying web game generation pipeline.</p></li><li><p>A rapid, handsfree way to develop taste.</p></li></ul><div><hr></div><h2>Contents</h2><ul><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/fatherhood-and-inspo">Fatherhood and Inspo</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/the-reluctant-web-developer">The Reluctant Web Developer</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/my-automation-pipeline">My Automation Pipeline</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/voice-prompting">Voice Prompting</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/claude-remote-control">Claude Remote Control</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/git-vercel-and-cloudflare">Git, Vercel, and Cloudflare</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/vr-psychosis">VR Psychosis</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/the-final-moving-part">The final moving part</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/building-my-own-vr-games">Building my own VR Games</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/star-contract">Star Crusader</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/iterating-towards-a-secondary-gameplay-loop">Iterating towards a secondary gameplay loop</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/rampart">Rampart</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/frostflame">FrostFlame</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/star-miner">Star Miner</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/balloon-thunderdome">Balloon Thunderdome</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/whoops-i-forgot-about-my-kids">Whoops, I forgot about my kids</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/197332025/last-orders">Last Orders &#127866;</a></p></li></ul><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Fatherhood and Inspo</h2><p>Allow me to jump back a few months in this deeply untethered narrative.</p><p>My first brush with this came not from nascent VR psychosis, but from wanting to be a fun dad. A fun dad <em>in tech</em>.</p><p>Many parents guiltily give their kids access to electronics when they need a breather. My daughter, between spats of dodgy YouTube slop, is partial to a balloon popping game. On a whim, I thought to myself: <em>this is total dogsh*t. I could do much better than this</em>. So I opened Claude and asked it to make a balloon popping iOS app with <em><strong>powerups</strong></em>.</p><p>My fatherly vision of giving my daughter a flavour of this impossible dream: to become equal to a lottery winner, dictating the terms of a video game built before her eyes&#8230; might have missed the mark. Teamwork. User research. Bonding. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NwOt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NwOt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png 424w, https://substackcdn.com/image/fetch/$s_!NwOt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png 848w, https://substackcdn.com/image/fetch/$s_!NwOt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png 1272w, https://substackcdn.com/image/fetch/$s_!NwOt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!NwOt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png" width="1456" height="788" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:788,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3691153,&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://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.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_!NwOt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png 424w, https://substackcdn.com/image/fetch/$s_!NwOt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png 848w, https://substackcdn.com/image/fetch/$s_!NwOt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png 1272w, https://substackcdn.com/image/fetch/$s_!NwOt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bab7333-5534-464d-8fea-d412c1a5dafc_2198x1190.png 1456w" sizes="auto, 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">Left: a balloon game that&#8217;s hot garbage. The rest: a f*cking rad balloon game. </figcaption></figure></div><p>Did my daughter start playing the upgraded version of her own accord? No. Did she like it more than the <em>(crap)</em> original? Also no. But I got some super useful lessons about performing user research on a 5-year-old:</p><blockquote><p><em>&#8220;Make it better. Make it less bad. It&#8217;s boring.&#8221;</em><br>&#8212;Lyra, 2026. Thanks, Lyra. </p></blockquote><div><hr></div><h2>The Reluctant Web Developer </h2><p>Later that weekend, at a kid&#8217;s birthday party, while trying to desperately find testers to get in on the balloon action, I had a crushing realisation: I can&#8217;t iterate as fast while tied to iOS.</p><p>Sitting in my home office, neglecting my kids, as I voice-prompt an agent to build a new power-up, is all well and good, but deeply limiting to iteration speed. </p><p>Apple doesn&#8217;t exactly help themselves here either. The web, for all its faults, is <em>(by a drastic margin)</em> the easiest way to deploy things. </p><p>Easier&#8230; after some annoying admin. </p><div><hr></div><h2>My Automation Pipeline</h2><p>Indulge me with another tangent, so I can set the scene. It&#8217;s relevant, I&#8217;m trying to explain the level of handsfreeness we&#8217;re playing with in 2026.</p><p>It was May 12th. My birthday. You can send me gifts <a href="https://blog.jacobstechtavern.com/subscribe">here</a> btw.</p><p>I was at the pub. A friend reminded me of a bit I used to do when I was 18 <em>(which, for the record, I do <strong>not</strong> remember)</em> which I employed in my eternal quest to be really annoying. When I&#8217;d make an edgy joke, I&#8217;d obnoxiously open an app called &#8220;Privilege Checker&#8221;&#8482; to l a checklist <em>(white, straight, cis, etc, etc)</em>, and, having checked my privilege, would deploy my moderately offensive joke. It&#8217;s all about the timing, you see?</p><p>I chuckled at the alleged memory, and then, in my brooding, mysterious way, walked outside for a moment. I came back a few minutes later and <a href="http://games.jacobstechtavern.com/privilege-checker">shoved this web app in my friend&#8217;s face</a>. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KPNo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KPNo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png 424w, https://substackcdn.com/image/fetch/$s_!KPNo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png 848w, https://substackcdn.com/image/fetch/$s_!KPNo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png 1272w, https://substackcdn.com/image/fetch/$s_!KPNo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!KPNo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png" width="1456" height="842" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:842,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2139701,&quot;alt&quot;:&quot;&quot;,&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://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!KPNo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png 424w, https://substackcdn.com/image/fetch/$s_!KPNo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png 848w, https://substackcdn.com/image/fetch/$s_!KPNo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png 1272w, https://substackcdn.com/image/fetch/$s_!KPNo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff21ffe30-0414-4e5a-a222-68d82585b8a8_1792x1036.png 1456w" sizes="auto, 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>I can&#8217;t really take credit for this: I own no inference clusters, I have no complex pipeline, and I&#8217;m not personally touching Git on a Friday night. I just orchestrate moving parts that greater minds created. I&#8217;m just the glue. Specifically, the glue is my mouth.</p><div><hr></div><h4><strong>Voice Prompting</strong></h4><p>Typing? Okay, grandad. Back to the nursing home with you.</p><p>It&#8217;s crazy how much my workflow has changed since adopting voice-dictation tools. The vast majority of my day-to-day work is meanderingly mumbling into a microphone, and then code comes out the other side. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YfXw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YfXw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png 424w, https://substackcdn.com/image/fetch/$s_!YfXw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png 848w, https://substackcdn.com/image/fetch/$s_!YfXw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png 1272w, https://substackcdn.com/image/fetch/$s_!YfXw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!YfXw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png" width="1456" height="754" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:754,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:133585,&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://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.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_!YfXw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png 424w, https://substackcdn.com/image/fetch/$s_!YfXw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png 848w, https://substackcdn.com/image/fetch/$s_!YfXw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png 1272w, https://substackcdn.com/image/fetch/$s_!YfXw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21f0483b-514c-4752-ba01-8d972e670daf_1784x924.png 1456w" sizes="auto, 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">Get on my level, bro</figcaption></figure></div><p>This is great for fast, messy iteration and field research. You emit a stream of consciousness and get something useful out the other side. Assuming there&#8217;s a model capable of interpreting your genius.</p><p>This is rad for the one-shot approach. But also great for iteration. </p><p>I get solid results with Wispr Flow and <a href="https://granola.ai/">Granola</a>. Wispr Flow is better for rapid live-testing loops where you&#8217;re tasting your own dog food and giving live shot-calling dictation. Granola is better for set-and-forget long-form user research.</p><div><hr></div><h4>Claude Remote Control</h4><p>This works with Codex too, but I try to save my credits for important stuff.</p><p>The <strong>/remote-control</strong> command, alongside caffeinate <em>(to stop your machine falling asleep), </em>become the poor man&#8217;s OpenClaw. You can prompt Claude in a folder on your machine from the app. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HpXB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HpXB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png 424w, https://substackcdn.com/image/fetch/$s_!HpXB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png 848w, https://substackcdn.com/image/fetch/$s_!HpXB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png 1272w, https://substackcdn.com/image/fetch/$s_!HpXB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!HpXB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png" width="1456" height="658" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:658,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:823786,&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://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.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_!HpXB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png 424w, https://substackcdn.com/image/fetch/$s_!HpXB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png 848w, https://substackcdn.com/image/fetch/$s_!HpXB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png 1272w, https://substackcdn.com/image/fetch/$s_!HpXB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64421e53-3455-4dbd-a0e3-ed00549b2b37_2358x1066.png 1456w" sizes="auto, 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>I say the poor man&#8217;s OpenClaw. It&#8217;s probably more like the poor man&#8217;s Mac Mini. I don&#8217;t <em>quite</em> feel dropping &#163;799 is worth the luxury of not closing my clamshell. </p><p>That said, once I got into the throes of VR psychosis, I got close.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7xDE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7xDE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7xDE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7xDE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7xDE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7xDE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg" width="1456" height="1139" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1139,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:176554,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!7xDE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7xDE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7xDE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7xDE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc834272f-e705-4759-89fc-b914158c5b4d_1662x1300.jpeg 1456w" sizes="auto, 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">Me trying not to buy a Mac mini after learning I can vibe-code VR games</figcaption></figure></div><p>Unshackled from my home office, I can dictate commands to my agent from the pub. Naturally, my agent has git access, able to push straight up to my repo and deploy.</p><div><hr></div><h4><strong>Git, Vercel, and Cloudflare</strong></h4><p>The 3 amigos. </p><p>Frankly, if you held a gun to my head, and told me to explain what Vercel does or you&#8217;ll blow my brains out, my last words would be a curse upon Vercel. I kinda reckon it hosts web servers, but don&#8217;t take my word for it. </p><p>With some slightly annoying admin, you can make it so every git push automatically goes through to Vercel and gets deployed. So&#8230; <em>I guess it&#8217;s a CI service?</em> </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wZ9p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wZ9p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png 424w, https://substackcdn.com/image/fetch/$s_!wZ9p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png 848w, https://substackcdn.com/image/fetch/$s_!wZ9p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png 1272w, https://substackcdn.com/image/fetch/$s_!wZ9p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!wZ9p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png" width="1456" height="441" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:441,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:411887,&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://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.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_!wZ9p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png 424w, https://substackcdn.com/image/fetch/$s_!wZ9p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png 848w, https://substackcdn.com/image/fetch/$s_!wZ9p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png 1272w, https://substackcdn.com/image/fetch/$s_!wZ9p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb65fbae3-c8eb-4a2e-92c3-dfe24778a4c2_2596x786.png 1456w" sizes="auto, 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>Cloudflare is a DDoS protection service, which for some reason also does domain registration. I set up a CNAME for the subdomain <strong><a href="https://games.jacobstechtavern.com/">games.jacobstechtavern.com</a></strong> that keeps my wildest ideas airgapped from my colleagues at Granola.</p><p>This basically means for my <strong><a href="https://jacobstechtavern.com/">jacobstechtavern.com</a> </strong>domain, I can easily create subdomains, and Vercel routes the deployed web pages there.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yDha!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yDha!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png 424w, https://substackcdn.com/image/fetch/$s_!yDha!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png 848w, https://substackcdn.com/image/fetch/$s_!yDha!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png 1272w, https://substackcdn.com/image/fetch/$s_!yDha!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!yDha!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png" width="1456" height="366" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:366,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:212322,&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://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.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_!yDha!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png 424w, https://substackcdn.com/image/fetch/$s_!yDha!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png 848w, https://substackcdn.com/image/fetch/$s_!yDha!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png 1272w, https://substackcdn.com/image/fetch/$s_!yDha!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F426b66fd-d70b-4c1a-adb5-24c9afaaec65_2998x754.png 1456w" sizes="auto, 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>Vercel and Cloudflare work together in ways I simply cannot comprehend, but they steam a good ham.</p><p>The point is that, now, the cost of deploying is zero. The cost of implementing is also zero. The cost of putting my thoughts into a computer is reduced to a moment of mumbling. Thus blurs the line between ideas in your head and working software in your hand. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!N7jF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!N7jF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!N7jF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!N7jF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!N7jF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!N7jF!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:1992621,&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://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!N7jF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!N7jF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!N7jF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!N7jF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed47f77e-0cd2-4d1a-86a4-518ad01a2553_1672x941.png 1456w" sizes="auto, 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 class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2><strong>VR Psychosis </strong></h2><p>Now I can explain the worst avoidable motion sickness I&#8217;ve ever given myself. </p><p>The fan-made <a href="https://www.youtube.com/watch?v=o9BlCYydll4&amp;t=71s">Attack on Titan VR game</a>. You fly around and kill big fellas with swords.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hjeA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hjeA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png 424w, https://substackcdn.com/image/fetch/$s_!hjeA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png 848w, https://substackcdn.com/image/fetch/$s_!hjeA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png 1272w, https://substackcdn.com/image/fetch/$s_!hjeA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!hjeA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2785682,&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://blog.jacobstechtavern.com/i/197332025?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.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_!hjeA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png 424w, https://substackcdn.com/image/fetch/$s_!hjeA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png 848w, https://substackcdn.com/image/fetch/$s_!hjeA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png 1272w, https://substackcdn.com/image/fetch/$s_!hjeA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f5ad78f-c65a-49e5-9a0e-686a198a8eaf_2110x1178.png 1456w" sizes="auto, 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>There are many degrees of freedom in VR: 2 triggers, plus face buttons, in each hand, with 360&#176; range of motion for each hand.</p><p>The traversal mechanics in Attack on Titan VR involved shooting a cable at a wall. The other trigger reels you in, building momentum. The simple physics alone here allowed for incredibly dynamic motion. I couldn&#8217;t stop playing, for hours, acrobatically spidermanning through a city, until I was completely <em>floored</em> by the motion sickness that had been building up as my brain fell over trying to reconcile my senses of sight and balance.</p><p>I digress. Traversal mechanics are fun. They are a huge component of a primary gameplay loop: the moment-to-moment enjoyment. See Mario 64: the devs <a href="https://shmuplations.com/mario64/">spent half of development</a> working on 3D motion, making the space fun to move around in. They didn&#8217;t even begin building the levels in Peach&#8217;s Castle until they perfected the ability to jump about and handle the camera in a 3D space. </p><div><hr></div><h4>The final moving part</h4><p>Now, a friend at work showed me something that he vibe coded that ties this all together.</p><p>He put a VR headset on my head and showed me a fun little space where I could shoot little Granola logos at targets. <em>We&#8217;ll find a business use case soon, I&#8217;m sure.</em> </p><p>He gave me an insight I didn&#8217;t know before: it turns out that it&#8217;s really easy to vibe VR games and deploy them online using Three.js, WebXR, and Vite. Whole VR games.  </p><p>It&#8217;s time to tie these threads together. </p><p>Automated web deployment.</p><p>Traversal mechanics.</p><p>Vibe-coded web-based VR games.</p>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/vr-psychosis">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Forbidden iOS 👹]]></title><description><![CDATA[7 more things Apple doesn't want you to know]]></description><link>https://blog.jacobstechtavern.com/p/forbidden-ios</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/forbidden-ios</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 16 Sep 2026 15:00:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!z7c5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z7c5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z7c5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!z7c5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!z7c5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!z7c5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z7c5!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:5609608,&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://blog.jacobstechtavern.com/i/206674838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z7c5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!z7c5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!z7c5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!z7c5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a10b9b4-ca75-4c7c-80a4-b4affb14ef04_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>To use a tired clich&#233;, I don&#8217;t like drawing inside the lines. This causes my 5-year-old to get <em>incredibly</em> f*cked off with me when I try and express myself. </p><p>Apple used to give you a book on your first day as an iOS engineer. <em><a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/">The Swift Programming Language</a></em>. Since Swift 5.8, it<a href="https://www.reddit.com/r/swift/comments/192tvaf/no_way_to_get_a_clean_the_swift_programming/">&#8217;s no longer available as a book</a>, because it would be over 4,000 pages. Chris Lattner is <a href="https://blog.jacobstechtavern.com/p/apple-is-killing-swift">rolling in his grave</a>.</p><p>This blog post is for devs who aren&#8217;t afraid to reach around in the septic tank behind the compiler to locate their ring. Who aren&#8217;t cowed by petty concerns like <em>mild</em> <em>App Review rejection risk</em>. Devs who, most importantly, have the <em>cojones </em>(the balls) to step outside the sandbox offered to you by a type-safe memory-safe baby-mode language.</p><p>Today I&#8217;m sharing 7 facets of iOS you might never have encountered before. Some are useful in your day-to-day. Most won&#8217;t be. But all teach an interesting lesson about iOS. </p><p><strong><a href="https://blog.jacobstechtavern.com/i/206674838/reflection-and-uinavigationbarpalette">Reflection &amp; _UINavigationBarPalette</a></strong><br>(<em><strong>Y</strong>ou can learn a lot about framework internals at runtime</em>.)</p><p><strong><a href="https://blog.jacobstechtavern.com/i/206674838/arm64-and-machine-code">ARM64 &amp; Machine Code</a></strong><br><em><strong>Y</strong>our code ultimately turns into low-level instructions, fed into a CPU.</em></p><p><strong><a href="https://blog.jacobstechtavern.com/i/206674838/mach-apis-and-the-cpu">Mach APIs &amp; the CPU</a></strong> <br><em><strong>I</strong>t&#8217;s possible to talk to hardware</em></p><p><strong><a href="https://blog.jacobstechtavern.com/i/206674838/dynamism-and-objcsetassociatedobject">Dynamism &amp; objc_setAssociatedObject</a></strong><br><em><strong>S</strong>wift is built on 5 decades of ObjC foundations</em></p><p><strong><a href="https://blog.jacobstechtavern.com/i/206674838/caemitterlayer">CAEmitterLayer &amp; Design Risks</a></strong> <br><em><strong>Y</strong>ou are just scratching the surface of what&#8217;s possible onscreen</em></p><p><strong><a href="https://blog.jacobstechtavern.com/i/206674838/unmanaged-swift-and-ancient-frameworks">Unmanaged Swift &amp; Ancient Frameworks</a></strong><br><em><strong>M</strong>ost of the underlying libraries in MacOS and iOS are C</em></p><p><strong><a href="https://blog.jacobstechtavern.com/i/206674838/uiportalview-and-liquid-glass">_UIPortalView &amp; Liquid Glass</a></strong><br><em><strong>L</strong>iquid Glass isn&#8217;t magic</em></p><div><hr></div><h2>Reflection &amp; _UINavigationBarPalette</h2><p><em><strong>Y</strong>ou can learn a lot about framework internals at runtime</em>.</p><p>Here&#8217;s a picture of me and <a href="https://x.com/SebJVidal">Seb Vidal</a> at a recent Apple event! </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!x5vk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!x5vk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic 424w, https://substackcdn.com/image/fetch/$s_!x5vk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic 848w, https://substackcdn.com/image/fetch/$s_!x5vk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic 1272w, https://substackcdn.com/image/fetch/$s_!x5vk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!x5vk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2270018,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/206674838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!x5vk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic 424w, https://substackcdn.com/image/fetch/$s_!x5vk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic 848w, https://substackcdn.com/image/fetch/$s_!x5vk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic 1272w, https://substackcdn.com/image/fetch/$s_!x5vk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c46ed46-add3-415e-b44c-69520d27387a_4896x3672.heic 1456w" sizes="auto, 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">You can see the back of Seb&#8217;s head approximately 2 rows in front of me </figcaption></figure></div><p>Seb knows more than anyone about UIKit internals, mostly through the most straightforward kind of reverse engineering: runtime reflection using Objective-C selectors. </p><p>Most of Apple&#8217;s frameworks are Obj-C based. Thank John Sculley, Steve Jobs, and NeXT for that. This makes frameworks like UIKit more-or-less transparent, for those with eyes to see.</p><p>Dynamically picking selector strings at runtime is the linchpin of private API work. In your Swift code, you never lost access to the Objective-C runtime: just call NSClassFromString, NSSelectorFromString, and all their friends to initialise underscored classes and &#8220;private&#8221; APIs.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">import UIKit

let tabs = AnimatedTabSelector(titles: ["For You", "Following"])
tabs.frame.size.height = 56

let cls = NSClassFromString("_UINavigationBarPalette") as! UIView.Type
let palette = cls.perform(NSSelectorFromString("alloc"))!.takeUnretainedValue()
    .perform(NSSelectorFromString("initWithContentView:"), with: tabs)!
    .takeUnretainedValue()

navigationItem.perform(NSSelectorFromString("_setBottomPalette:"), with: palette)</code></pre></div><p>Seb&#8217;s pride is the _UINavigationBarPalette, which lets you attach arbitrary views onto a navigation bar, but <em>without</em> losing the useful a11y, size transitions, and automatic layout that makes most custom UI so Temu-esque. Apple uses this a ton internally for apps like Calendar, Fitness, Photos, and Messages but jealously guards it from mortals.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;230b05f2-8bb1-49a8-b2d7-d4c5b0285fd6&quot;,&quot;duration&quot;:null}"></div><p>See Seb&#8217;s original tweet (and give him a follow!) here:</p><div class="twitter-embed" data-attrs="{&quot;url&quot;:&quot;https://x.com/SebJVidal/status/1748659522455937213&quot;,&quot;full_text&quot;:&quot;&#9888;&#65039; Private API of the Day: _UINavigationBarPalette\n\nThis let's you add a \&quot;palette\&quot; below a UINavigationBar as per Calendar and Fitness.\n\nI'm by no means the first to discover this, but here's a fully Swift implementation, no Obj-C and bridging header. Probably App Store safe with &quot;,&quot;username&quot;:&quot;SebJVidal&quot;,&quot;name&quot;:&quot;Seb Vidal&quot;,&quot;profile_image_url&quot;:&quot;https://pbs.substack.com/profile_images/1417933554932146176/O36-0MLR_normal.jpg&quot;,&quot;date&quot;:&quot;2024-01-20T10:51:33.000Z&quot;,&quot;photos&quot;:[{&quot;img_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!xYmO!,w_1028,c_limit,f_auto,q_auto:best,fl_progressive:steep/l_play_button_usfui2,w_88,e_colorize:0/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F__ss-rehost__tw-video-preview-7_1748658718302998528.jpg&quot;,&quot;link_url&quot;:&quot;https://t.co/Se0Me4Uu43&quot;}],&quot;quoted_tweet&quot;:{},&quot;reply_count&quot;:12,&quot;retweet_count&quot;:14,&quot;like_count&quot;:385,&quot;impression_count&quot;:93138,&quot;expanded_url&quot;:null,&quot;video_url&quot;:&quot;https://video.twimg.com/ext_tw_video/1748658718302998528/pu/vid/avc1/958x720/VrqevlaeiNll9mI5.mp4?tag=14&quot;,&quot;video_preview_media_key&quot;:null,&quot;belowTheFold&quot;:true}" data-component-name="Twitter2ToDOM"></div><p>App Review can always ding you if you muck around with private APIs, but <em>(we were not lawyers)</em> Seb reckons you&#8217;re basically probably alright. More or less. </p><blockquote><p><em>I&#8217;ve chatted (recently!) about using private CAFilter API to add design flourishes to nav bars before:</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;eab33e2e-db5d-4327-821f-54d7b3b6805b&quot;,&quot;caption&quot;:&quot;At a corporate motivational event once, I found out it&#8217;s good to kiss your customers.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;My Top 3 Design &#8220;Kisses&#8221; I Like to Add to Every App&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Master iOS. Boost your salary. Join 100,000 senior Swift devs learning advanced concurrency, SwiftUI, and iOS performance for 10 minutes a week. Sign up free today!&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2026-07-22T15:00:19.001Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!SZ-p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif&quot;,&quot;cover_image_alt&quot;:null,&quot;cover_image_crop&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/design-kisses&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:203047335,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:8,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><em>If you want to go harder with reverse engineering, there&#8217;s a way to do so with pure-Swift frameworks as well.</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;5a50f5d6-7afc-4c77-b311-baced53794d1&quot;,&quot;caption&quot;:&quot;I&#8217;m religious when it suits me.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;How do researchers reverse-engineer private frameworks?&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Master iOS. Boost your salary. Join 100,000 senior Swift devs learning advanced concurrency, SwiftUI, and iOS performance for 10 minutes a week. Sign up free today!&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2025-10-28T16:02:55.430Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Uymn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54b79f9d-ca27-4350-8a74-41a677768088_1680x1200.png&quot;,&quot;cover_image_alt&quot;:null,&quot;cover_image_crop&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/reverse-engineer-private-frameworks&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:176832931,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:23,&quot;comment_count&quot;:2,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div></blockquote><div><hr></div><h2>ARM64 &amp; Machine Code </h2><p><em><strong>Y</strong>our code ultimately turns into low-level instructions, fed into a CPU.</em></p><p>This is another hidden gem I found <a href="https://x.com/p_x9/status/2061028095440957683">via a Tweet</a>: the incredibly cracked <a href="https://github.com/p-x9">p-x9</a> created a library and a macro that lets you run raw ARM64 machine code inside your apps. It&#8217;s not exactly a built-in Apple framework, but learning about assembly is a bucketlist item for all serious engineers.</p><p>The library is two components: </p><p><strong><a href="https://github.com/swiftbin/swift-assembly">swift-assembly</a></strong> does the complicated ARM instruction set translation: parsing assembly strings, validating instructions &amp; registering against its <a href="https://github.com/swiftbin/swift-assembly/blob/main/Sources/Assembly/ARM64/A64Model.swift">ARM64 instruction catalog</a>, then encodes them into machine code bytes: <strong>[UInt8]</strong>.</p><p><strong><a href="https://github.com/p-x9/swift-asm-macro">swift-asm-macro</a></strong> is the tidy API surface; running during compile-time, calling ARM64Assembler.assemble(source) in swift-assembly, and emitting them into the executable.</p><p>Here&#8217;s a couple of simple examples to get you going with instructions and registers: </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:&quot;cc7864a1-baea-41d9-a830-9ef334ceec0e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@Asm(
    """
    add x0, x0, x1,
    ret
    """,
    arch: .arm64
)
func add(_ lhs: Int64, _ rhs: Int64) -&gt; Int64</code></pre></div><p>This sums the input values in the x0 and x1 registers, places the result into x0, and returns it.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@Asm(
    """
    cmp x0, x1,
    csel x0, x0, x1, ge,
    ret
    """,
    arch: .arm64
)
func armMaximum(_ lhs: Int64, _ rhs: Int64) -&gt; Int64</code></pre></div><p>cmp and csel compare the inputs in the x0 and x1 registers, place the larger value into x0, then return it.</p><p>The library assembles these assembly instructions at compile-time and uses <a href="https://github.com/swiftlang/swift-evolution/blob/main/proposals/0492-section-control.md">section placement control</a> to insert them into your app&#8217;s compiled MachO binary, called via a @convention(c) function pointer.</p><p>The expanded macro itself is pretty cool, if you&#8217;re a huge nerd:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:&quot;fd22be38-f2a8-4bc1-828c-56d2f6754afb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@Asm(
    """
    add x0, x0, x1,
    ret
    """,
    arch: .arm64
)
func add(_ lhs: Int64, _ rhs: Int64) -&gt; Int64
#if arch(arm64)
@used
@section("__TEXT,__text")
nonisolated(unsafe) var __asm_add: (UInt32, UInt32) = (
    0x8b010000,
    0xd65f03c0
)
#else
#error("@Asm storage __asm_add was generated for arm64.")
#endif
{
    #if arch(arm64)
    typealias __AsmFn = @convention(c) (Int64, Int64) -&gt; Int64
    let f = withUnsafePointer(to: &amp;__asm_add) {
        unsafeBitCast($0, to: __AsmFn.self)
    }
    return f(lhs, rhs)
    #else
    #error("@Asm function add was generated for arm64.")
    #endif
}</code></pre></div><p>We can create a slightly more interesting example: a screenshot difference engine. I&#8217;m <a href="https://blog.jacobstechtavern.com/p/my-secret-plot-to-kill-swiftui">re-writing parts of Granola in UIKit</a>, and want to work out whether we&#8217;re pixel-perfect or not. This function compares every colour byte across 2 images, and returns red if they aren&#8217;t equal:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">@Asm(
    """
    movi v2.4s, #0                     // start four running pixel counters at zero

    loop:
      ldr q0, [x0], #16                // load four before pixels; advance x0
      ldr q1, [x1], #16                // load four after pixels; advance x1
      eor v0.16b, v0.16b, v1.16b       // matching bits become 0; differences become 1
      cmtst v0.4s, v0.4s, v0.4s        // changed pixels become 0xffffffff
      str q0, [x3], #16                // write four pixels into the output mask
      sub v2.4s, v2.4s, v0.4s          // subtracting -1 adds one to that lane's count
      subs x2, x2, #1                  // consume one four-pixel batch and set flags
      b.ne loop                        // repeat until every batch is processed

    addv s2, v2.4s                     // combine the four lane counters into one total
    umov w0, v2.s[0]                   // move the total into the ARM64 return register
    ret                                // return to Swift
    """,
    arch: .arm64
)
func armPixelDiff(
    _ before: UnsafePointer&lt;UInt8&gt;,
    _ after: UnsafePointer&lt;UInt8&gt;,
    _ batchCount: UInt64,
    _ mask: UnsafeMutablePointer&lt;UInt8&gt;
) -&gt; UInt64</code></pre></div><blockquote><p><em>See the full Github Gist, including full CoreGraphics code, <a href="https://gist.github.com/jacobsapps/14d8b3219300699e2c00899cd140f9f0">here</a>.</em></p></blockquote><p>And when we look at the results&#8230; perhaps we still have some way to go!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LARv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LARv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png 424w, https://substackcdn.com/image/fetch/$s_!LARv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png 848w, https://substackcdn.com/image/fetch/$s_!LARv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png 1272w, https://substackcdn.com/image/fetch/$s_!LARv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!LARv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png" width="1456" height="994" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:994,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:624499,&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://blog.jacobstechtavern.com/i/206674838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.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_!LARv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png 424w, https://substackcdn.com/image/fetch/$s_!LARv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png 848w, https://substackcdn.com/image/fetch/$s_!LARv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png 1272w, https://substackcdn.com/image/fetch/$s_!LARv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbca241d-d097-4c9d-9bed-2e79a07df7f6_1946x1328.png 1456w" sizes="auto, 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>Let&#8217;s be clear: the Swift compiler is pretty well-optimised, and so you will be unlikely to need to use raw assembly to improve performance. This example is fun, but way slower than basic CoreImage APIs that get to use the GPU.</p><div class="callout-block" data-callout="true"><p>2 chapters is enough for most people. But if you&#8217;re in my top 5% smartest subscribers, you&#8217;ll want to read the rest.</p><p>If you want the subsequent 72% of the article, you know what to do:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=206674838&quot;,&quot;text&quot;:&quot;Get 14 day free trial&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=206674838"><span>Get 14 day free trial</span></a></p><p>Paid members get lots of nice things:</p><p>&#9875;&#65039; Access my <a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 50+ paywalled articles</a> (including this one)<br>&#128640; Read <a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a> a month before anyone else<br>&#129525; Master concurrency with my <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a> and <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br>&#129489;&#8205;&#128640; Get a free copy of my new eBook, &#8220;<a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a>&#8221;<br>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</p></div>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/forbidden-ios">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[App Size 101: Drop 40% in an afternoon]]></title><description><![CDATA[Fix the 5 most common low-hanging fruit]]></description><link>https://blog.jacobstechtavern.com/p/app-size-101</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/app-size-101</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 09 Sep 2026 15:03:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!v7pU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!v7pU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!v7pU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png 424w, https://substackcdn.com/image/fetch/$s_!v7pU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png 848w, https://substackcdn.com/image/fetch/$s_!v7pU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png 1272w, https://substackcdn.com/image/fetch/$s_!v7pU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!v7pU!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:1569277,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!v7pU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png 424w, https://substackcdn.com/image/fetch/$s_!v7pU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png 848w, https://substackcdn.com/image/fetch/$s_!v7pU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png 1272w, https://substackcdn.com/image/fetch/$s_!v7pU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a217865-6dde-4719-a1e5-332969065fac_1484x1060.png 1456w" sizes="100vw" fetchpriority="high"></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>When I start a new job, I like to quickly make a name for myself as the &#8220;app size&#8221; guy.</p><p>This means proactively investigating the app bundle, proposing steps to make it slimmer, and implementing them in our next release. I do this for a few reasons:</p><ul><li><p>It&#8217;s easy.</p></li><li><p>It&#8217;s quick. </p></li><li><p>It&#8217;s fun.</p></li><li><p>Hopefully, nobody else thought to do it yet *<em>if it&#8217;s a startup</em>. </p></li><li><p>It&#8217;s visible <em>and</em> comprehensible to non-technical stakeholders.</p><ul><li><p><em>Especially</em> if your work impacts install rate.</p></li></ul></li></ul><p>Today I&#8217;m showing you a new, free, open-source, fully private, in-browser tool to help you optimise your own app bundle. I&#8217;ll explain how it works, and along the journey we&#8217;ll go through a real-life worked example: <strong>how I optimised Granola&#8217;s iOS app size by ~40% in my first week</strong>.</p><p>By the end, you&#8217;ll have a clear to-do list to optimise your own app and look like a total genius in the process.</p><div><hr></div><h5>Sponsored Link</h5><blockquote><h4><a href="https://www.shipaton.com/?utm_medium=sponsored&amp;utm_source=JacobsTechTavern&amp;utm_campaign=newsletter&amp;utm_content=shipaton-september">Recently shipped an app? Enter it in Shipaton 2026</a></h4><p>Recently launched a mobile app? <span>Put it in the spotlight </span>at Shipaton <span>2026 &#8212; </span>RevenueCat&#8217;s global hackathon for builders who ship real apps to real stores. Compete for over $1M in prizes and show the world what you built. <a href="https://www.shipaton.com/?utm_medium=sponsored&amp;utm_source=JacobsTechTavern&amp;utm_campaign=newsletter&amp;utm_content=shipaton-september">Entries close September 30</a>. </p></blockquote><div><hr></div><h2>Contents</h2><ul><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/openbundle">OpenBundle</a> </p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/how-it-works">How it works</a> </p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/the-low-hanging-fruit">The Low-Hanging Fruit</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/before">Before &amp; After</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/duplication">Duplication</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/asset-sizing">Asset Sizing</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/binary-symbols">Binary Symbols</a> </p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/stripping-binary-symbols">Stripping binary symbols</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/symbol-metadata">Symbol metadata</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/dynamic-vs-static-frameworks">Dynamic vs Static Frameworks</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/swift-optimisation-mode">Swift Optimisation Modes</a> </p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/automation-and-regressions">Automation &amp; Regressions</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/how-to-spy-on-your-competitors">How to spy on your competitors</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/139976243/last-orders">Last Orders &#127866;</a></p></li></ul><div><hr></div><h2>OpenBundle</h2><p>OpenBundle is a cool new tool that analyses your app bundle and gives you recommendations for the most impactful ways you can optimise your app size.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hoNv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hoNv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png 424w, https://substackcdn.com/image/fetch/$s_!hoNv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png 848w, https://substackcdn.com/image/fetch/$s_!hoNv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png 1272w, https://substackcdn.com/image/fetch/$s_!hoNv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!hoNv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png" width="1456" height="634" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:634,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1694263,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.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_!hoNv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png 424w, https://substackcdn.com/image/fetch/$s_!hoNv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png 848w, https://substackcdn.com/image/fetch/$s_!hoNv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png 1272w, https://substackcdn.com/image/fetch/$s_!hoNv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa761e309-2789-409b-b0d5-82fa1d267877_2562x1116.png 1456w" sizes="auto, 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>It&#8217;s <a href="https://github.com/jacobsapps/OpenBundle">open source</a>, lives entirely in your <a href="https://wwdc.jacobstechtavern.com/openbundle/">browser</a>, works fully offline, and <strong>has no backend</strong>, so your privacy is guaranteed.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://wwdc.jacobstechtavern.com/openbundle/&quot;,&quot;text&quot;:&quot;View OpenBundle&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://wwdc.jacobstechtavern.com/openbundle/"><span>View OpenBundle</span></a></p><h4>How it works</h4><p>An .ipa is basically a .zip file containing your app: code, frameworks, and resources. OpenBundle runs in your browser <em>(running CPython via Pyodide)</em> to expand the archive into a temporary filesystem, and processes every file to record their:</p><ul><li><p>path in the bundle</p></li><li><p>size (zip-compressed and uncompressed)</p></li><li><p>SHA-256 hash</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a8dq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a8dq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png 424w, https://substackcdn.com/image/fetch/$s_!a8dq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png 848w, https://substackcdn.com/image/fetch/$s_!a8dq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png 1272w, https://substackcdn.com/image/fetch/$s_!a8dq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!a8dq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png" width="1456" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&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="" title="" srcset="https://substackcdn.com/image/fetch/$s_!a8dq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png 424w, https://substackcdn.com/image/fetch/$s_!a8dq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png 848w, https://substackcdn.com/image/fetch/$s_!a8dq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png 1272w, https://substackcdn.com/image/fetch/$s_!a8dq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbdee7f8-af8b-45ff-940b-718fac645cae_1600x791.png 1456w" sizes="auto, 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><a href="https://blog.jacobstechtavern.com/p/reverse-engineer-private-frameworks">Mach-O files</a>, containing compiled Swift code, are parsed to read segments &amp; sections, symbol tables, load commands, dyld export tries, linked libraries, run paths, build versions, encryption states, code signatures and entitlements. </p><p>Asset catalogues are parsed <em>(using Rust tooling in WebAssembly)</em>, exposing asset names, dimensions, scales, encodings, opacity, sizes and image payloads. Browser image decoders can then lazily display previews, content hashes and real compression measurements.</p><p>OpenBundle now reconstructs the bundle&#8217;s directory hierarchy: Compiled code and assets are summed up to compute their parent folder&#8217;s size, and laid out in a hierarchical treemap: area represents data size.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Nlk3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Nlk3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png 424w, https://substackcdn.com/image/fetch/$s_!Nlk3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png 848w, https://substackcdn.com/image/fetch/$s_!Nlk3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png 1272w, https://substackcdn.com/image/fetch/$s_!Nlk3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Nlk3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png" width="1456" height="725" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:725,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2327570,&quot;alt&quot;:&quot;&quot;,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Nlk3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png 424w, https://substackcdn.com/image/fetch/$s_!Nlk3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png 848w, https://substackcdn.com/image/fetch/$s_!Nlk3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png 1272w, https://substackcdn.com/image/fetch/$s_!Nlk3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd61fb8fb-a48b-4c2a-a085-03642c91885b_2758x1374.png 1456w" sizes="auto, 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><blockquote><p><em>If you think Elon couldn&#8217;t get any worse, he built a bloody React Native app</em></p></blockquote><p>The rest of this article takes you through a real-life worked example of optimising an app bundle, which I implemented in my first week at Granola.</p><div><hr></div><h2>The Low-Hanging Fruit &#127826;</h2><p>Here&#8217;s your actual motivation: I wasn&#8217;t exaggerating above; between waxing lyrical about someday <a href="https://blog.jacobstechtavern.com/p/my-secret-plot-to-kill-swiftui">migrating away from SwiftUI</a> and <a href="https://blog.jacobstechtavern.com/p/modular-ios-architecture">re-architecting our app modules</a>, I did try to actually contribute something concrete in my first week at Granola.</p><p>Let&#8217;s look at some bundle-size X-rays. There are 2 numbers we care about: </p><p>Install size is how much disk space the thinned variant of your app takes up (before documents or data). Download size is the minimum amount of over-the-air network data required to install.</p><h4><strong>Before</strong></h4><p><strong>80.9 MB </strong>install<br><strong>48.4 MB </strong>download</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z0YL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z0YL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png 424w, https://substackcdn.com/image/fetch/$s_!z0YL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png 848w, https://substackcdn.com/image/fetch/$s_!z0YL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png 1272w, https://substackcdn.com/image/fetch/$s_!z0YL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!z0YL!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png" width="1200" height="750.8241758241758" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:911,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:2311992,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z0YL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png 424w, https://substackcdn.com/image/fetch/$s_!z0YL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png 848w, https://substackcdn.com/image/fetch/$s_!z0YL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png 1272w, https://substackcdn.com/image/fetch/$s_!z0YL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21bf5601-7806-42f0-9c66-e24ade9325ab_1972x1234.png 1456w" sizes="auto, 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><em>3.5 hours of work later&#8230;</em></p><h4>After</h4><p><strong>58.8 MB </strong>install <em>(27% less)</em><br><strong>28.1 MB </strong>download <em>(42% less)</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vpoU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vpoU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png 424w, https://substackcdn.com/image/fetch/$s_!vpoU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png 848w, https://substackcdn.com/image/fetch/$s_!vpoU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png 1272w, https://substackcdn.com/image/fetch/$s_!vpoU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!vpoU!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png" width="1200" height="609.8901098901099" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:740,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:2579468,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vpoU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png 424w, https://substackcdn.com/image/fetch/$s_!vpoU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png 848w, https://substackcdn.com/image/fetch/$s_!vpoU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png 1272w, https://substackcdn.com/image/fetch/$s_!vpoU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86584f8f-4f79-4da2-aae1-e6fdc80672c0_2450x1246.png 1456w" sizes="auto, 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>I achieved this through 5 straightforward fixes: low-hanging fruit that can apply to nearly every app:</p><ul><li><p>Handling duplication  </p></li><li><p>Optimising asset sizing</p></li><li><p>Removing binary symbols and symbol metadata</p></li><li><p>Choosing between dynamic and static </p></li><li><p>(if needed) picking your Swift optimisation modes </p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0LEr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0LEr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png 424w, https://substackcdn.com/image/fetch/$s_!0LEr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png 848w, https://substackcdn.com/image/fetch/$s_!0LEr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png 1272w, https://substackcdn.com/image/fetch/$s_!0LEr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!0LEr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png" width="1456" height="513" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:513,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1820761,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.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_!0LEr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png 424w, https://substackcdn.com/image/fetch/$s_!0LEr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png 848w, https://substackcdn.com/image/fetch/$s_!0LEr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png 1272w, https://substackcdn.com/image/fetch/$s_!0LEr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8ba2690-04bc-4fc0-85ea-523822b95e1b_2670x940.png 1456w" sizes="auto, 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 class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Duplication </h2><p>This is simultaneously the most common app sizing issue, usually the heftiest, and most obvious. Fortunately, it&#8217;s also easy to fix with zero repercussions (usually!).</p><p>I noticed that Granola had a lot of asset catalogues highlighted in red, flagging them as duplicates. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mWyl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mWyl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png 424w, https://substackcdn.com/image/fetch/$s_!mWyl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png 848w, https://substackcdn.com/image/fetch/$s_!mWyl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png 1272w, https://substackcdn.com/image/fetch/$s_!mWyl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!mWyl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png" width="1456" height="743" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:743,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2793588,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.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_!mWyl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png 424w, https://substackcdn.com/image/fetch/$s_!mWyl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png 848w, https://substackcdn.com/image/fetch/$s_!mWyl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png 1272w, https://substackcdn.com/image/fetch/$s_!mWyl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2191385-4b1e-4ddd-88fa-123d7e7ffce4_2492x1272.png 1456w" sizes="auto, 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>Lo and behold, this is directly flagged in the recommendations.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mDkK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mDkK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png 424w, https://substackcdn.com/image/fetch/$s_!mDkK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png 848w, https://substackcdn.com/image/fetch/$s_!mDkK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png 1272w, https://substackcdn.com/image/fetch/$s_!mDkK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!mDkK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png" width="1456" height="519" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:519,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1218458,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.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_!mDkK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png 424w, https://substackcdn.com/image/fetch/$s_!mDkK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png 848w, https://substackcdn.com/image/fetch/$s_!mDkK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png 1272w, https://substackcdn.com/image/fetch/$s_!mDkK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8f82256-271f-42a8-93bc-206f2e6ec76f_2114x754.png 1456w" sizes="auto, 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>What&#8217;s going on here?</p><p>Consider you have a second target in your app: a watch app, a widget, or perhaps a notification service extension. This often shares UI elements from your main app, usually a big shared UILibrary or DesignSystem module. </p><p>This is also, probably, where your biggest shared asset catalog lives.</p><p>Are you getting it? </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sVBk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sVBk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png 424w, https://substackcdn.com/image/fetch/$s_!sVBk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png 848w, https://substackcdn.com/image/fetch/$s_!sVBk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png 1272w, https://substackcdn.com/image/fetch/$s_!sVBk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!sVBk!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png" width="1200" height="606.5934065934066" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/129b7885-6457-41dc-9723-ce846fc011de_1780x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:736,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:113930,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sVBk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png 424w, https://substackcdn.com/image/fetch/$s_!sVBk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png 848w, https://substackcdn.com/image/fetch/$s_!sVBk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png 1272w, https://substackcdn.com/image/fetch/$s_!sVBk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F129b7885-6457-41dc-9723-ce846fc011de_1780x900.png 1456w" sizes="auto, 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>You can fix this pretty easily by splitting shared Assets out into its own module, allowing the other target to import our design system code without statically copying the whole asset repo.</p><p>Or, bear with me here, if you prefer to avoid mucking around with your modular architecture and globally refactoring your assets, you can opt to suffer a <em>tiny</em> amount of code duplication to get your design system code, if you choose to accept the maintenance burden to keep them in sync <em>(or want to jerry-rig some tooling to flag up changes)</em>.</p><p>It&#8217;s possible to share code between targets <em>without</em> duplication if we use <strong>dynamic linking</strong> - more on this later - but asset catalogs quite enjoy being static and are kind of a <a href="https://www.emergetools.com/blog/posts/make-your-ios-app-smaller-with-dynamic-frameworks">pain in the bum to convert</a> into dynamic xcframeworks.</p><p>Duplication issues aren&#8217;t always trivial to fix, mind you.</p><p>Perhaps you share a full feature betwixt app and app clip, with a very deep underlying dependency graph. I once had to <a href="https://blog.jacobstechtavern.com/i/149143793/splitting-up-the-rolls-module">split a large existing module into 2 parts</a> for exactly this reason, so one half fit onto a tiny 15MB app clip.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gU2Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gU2Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp 424w, https://substackcdn.com/image/fetch/$s_!gU2Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp 848w, https://substackcdn.com/image/fetch/$s_!gU2Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp 1272w, https://substackcdn.com/image/fetch/$s_!gU2Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!gU2Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp" width="1261" height="410" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:410,&quot;width&quot;:1261,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24940,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gU2Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp 424w, https://substackcdn.com/image/fetch/$s_!gU2Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp 848w, https://substackcdn.com/image/fetch/$s_!gU2Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp 1272w, https://substackcdn.com/image/fetch/$s_!gU2Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00199003-9e6e-403c-a24e-640d2f86518f_1261x410.webp 1456w" sizes="auto, 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><em>I almost didn&#8217;t mention, probably, the most common form of duplication: flat-out using the same asset in multiple asset catalogs across modules. This isn&#8217;t ideal, and you should probably find a lowest-common denominator module to share the asset, but also unless it&#8217;s a video the difference will be pretty negligible.</em></p><h4>How OpenBundle does it</h4><p>OpenBundle checks for duplication by hashing each file, and grouping these by their runtime bundle and byte size in a hash table.</p><p>If a bundle contains the same hash with the same size, we can pretty confidently highlight them as duplicates. If multiple bundles contain the same hash, it flags them for manual review.</p><div><hr></div><h2>Asset Sizing </h2><p>This is the most obvious optimisation once I point it out to you, but there is a bit of nuance to unpack here.</p><p>Often the biggest optimisation we can recommend is simply compressing our images more efficiently, for example from <strong>png</strong> to <strong>heic</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7IFl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7IFl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png 424w, https://substackcdn.com/image/fetch/$s_!7IFl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png 848w, https://substackcdn.com/image/fetch/$s_!7IFl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png 1272w, https://substackcdn.com/image/fetch/$s_!7IFl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7IFl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png" width="1456" height="530" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:530,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1255081,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.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_!7IFl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png 424w, https://substackcdn.com/image/fetch/$s_!7IFl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png 848w, https://substackcdn.com/image/fetch/$s_!7IFl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png 1272w, https://substackcdn.com/image/fetch/$s_!7IFl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a09c092-8bb9-429c-958b-27c56ca20aca_2114x770.png 1456w" sizes="auto, 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>Make sure to look carefully at the Assets.car unbundling in the treemap diagram directly, since you often find a few images taking up &#8216;surprisingly&#8217; large amounts of space. Audit a few images yourself and work out if you can downscale or compress them without losing any visual fidelity.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EZ_X!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EZ_X!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png 424w, https://substackcdn.com/image/fetch/$s_!EZ_X!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png 848w, https://substackcdn.com/image/fetch/$s_!EZ_X!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png 1272w, https://substackcdn.com/image/fetch/$s_!EZ_X!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!EZ_X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png" width="1456" height="746" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:746,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2524151,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.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_!EZ_X!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png 424w, https://substackcdn.com/image/fetch/$s_!EZ_X!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png 848w, https://substackcdn.com/image/fetch/$s_!EZ_X!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png 1272w, https://substackcdn.com/image/fetch/$s_!EZ_X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc47ce9b6-ceba-4e1f-ae93-1011935acbac_2476x1268.png 1456w" sizes="auto, 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">I found 2 graphics taking up 5MB. This is probably overkill for a widget.</figcaption></figure></div><p>Back in my first startup, Carbn, we stupidly exported some illustration assets as SVG, dramatically expanding these small images to insane 4MB sizes in the bundle. A very easy fix that saved us ~25MB in a single day.</p><p>Naturally, when compressing image assets, you probably want a designer to sign off on it: throw a side-by-side before and after onto an HTML page to ensure no quality is lost. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LkvB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LkvB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png 424w, https://substackcdn.com/image/fetch/$s_!LkvB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png 848w, https://substackcdn.com/image/fetch/$s_!LkvB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png 1272w, https://substackcdn.com/image/fetch/$s_!LkvB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!LkvB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png" width="1456" height="1118" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1118,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1243692,&quot;alt&quot;:&quot;&quot;,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!LkvB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png 424w, https://substackcdn.com/image/fetch/$s_!LkvB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png 848w, https://substackcdn.com/image/fetch/$s_!LkvB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png 1272w, https://substackcdn.com/image/fetch/$s_!LkvB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5fd74101-3e7e-429f-8996-9edf5a911db6_1816x1394.png 1456w" sizes="auto, 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>There is, ultimately, a trade-off before blindly replacing all your png with heic. The latter, while more space-efficient, can decode from disk <a href="https://github.com/dreampiggy/ModernImageFormatBenchmark">a lot more slowly</a>. As always, profile your code before listening to some guy on the internet. </p><p>Here&#8217;s an easy heuristic: <a href="https://developer.apple.com/documentation/uikit/uiimage">PNG is great for interface assets</a> and <a href="https://developer.apple.com/videos/play/wwdc2019/419/">HEIC is best for photographic assets</a>. </p><p>There are a few other image optimisations to consider:</p><ul><li><p>Avoid loose image assets in the bundle: asset catalogs enable app thinning.</p></li><li><p>Videos are always heavy: work out the minimum resolution that still looks good on an iPhone or iPad screen.</p></li><li><p>Many assets can be offloaded via <a href="https://developer.apple.com/documentation/backgroundassets">background assets</a>, <a href="https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/index.html">on-demand resources</a> or your own CDN. </p></li><li><p>Assets are more than images and videos: if your font file is taking up 500kB, it probably contains letters from Greek, Cyrillic, or Arabic alphabets. Unless you&#8217;re very well-localised, you can open up the .otf in FontForge and trim the file yourself.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!az4i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!az4i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png 424w, https://substackcdn.com/image/fetch/$s_!az4i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png 848w, https://substackcdn.com/image/fetch/$s_!az4i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png 1272w, https://substackcdn.com/image/fetch/$s_!az4i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!az4i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png" width="1331" height="576" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:576,&quot;width&quot;:1331,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:178033,&quot;alt&quot;:&quot;&quot;,&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://blog.jacobstechtavern.com/i/149143793?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!az4i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png 424w, https://substackcdn.com/image/fetch/$s_!az4i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png 848w, https://substackcdn.com/image/fetch/$s_!az4i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png 1272w, https://substackcdn.com/image/fetch/$s_!az4i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F63e6fcff-83df-4511-8ff3-7a03c712c5f8_1331x576.png 1456w" sizes="auto, 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>Fonts are handily highlighted in purple so you can easily investigate. Unless I&#8217;m really trying to squeeze the optimisation lemon I won&#8217;t worry too much if a font is under 200kB. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_KGt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_KGt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png 424w, https://substackcdn.com/image/fetch/$s_!_KGt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png 848w, https://substackcdn.com/image/fetch/$s_!_KGt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!_KGt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!_KGt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png" width="1456" height="924" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:924,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2012488,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.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_!_KGt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png 424w, https://substackcdn.com/image/fetch/$s_!_KGt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png 848w, https://substackcdn.com/image/fetch/$s_!_KGt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png 1272w, https://substackcdn.com/image/fetch/$s_!_KGt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aa3923-6d2b-4c71-855c-7478cbf5c09c_1986x1260.png 1456w" sizes="auto, 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><h4>How OpenBundle does it</h4><p>OpenBundle measures potential compression savings the obvious way: it picks the 80 largest assets, and generates an 85%-quality HEIC <em>(or, if there is no alpha channel, JPEG)</em> and checks the potential saving. It also searches for loose assets by finding files with @1x, @2x and @3x names and computing the difference when the smaller ones are thinned out.</p><div><hr></div><h2>Binary Symbols </h2><p>Binary Symbols and Binary Symbol Metadata are the twin bundle-nerd issues that cruft up your bundle due to some quirks of the compilation process. If you know what you&#8217;re doing, they can be removed &#8220;<strong>for free&#8221;</strong> without changing your app.</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_!dkfi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png 424w, https://substackcdn.com/image/fetch/$s_!dkfi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png 848w, https://substackcdn.com/image/fetch/$s_!dkfi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png 1272w, https://substackcdn.com/image/fetch/$s_!dkfi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!dkfi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png" width="1456" height="272" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:272,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:645950,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.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_!dkfi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png 424w, https://substackcdn.com/image/fetch/$s_!dkfi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png 848w, https://substackcdn.com/image/fetch/$s_!dkfi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png 1272w, https://substackcdn.com/image/fetch/$s_!dkfi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89cca2ab-e66d-4b91-8452-1d34e9d730bc_2130x398.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>But what are binary symbols? </p><p>&#8220;Symbols&#8221; are names attached to functions, classes and variables inside your compiled Mach-O binary. For a function doSomething(), it might be stored under a slightly mangled name like <code>_$s5MyApp11doSomethingyyF</code>.</p><p>The final stage of Swift compilation, static linking, resolves references in each binary, and notes down imports from external frameworks.</p><p>At launch, dyld (the dynamic linker) maps the app and frameworks into virtual memory, rebases these addresses, and binds the external imports from before into actual runtime memory addresses. </p><p>__LINKEDIT is a segment of your binary that contains data produced by the static linker, and used by the dynamic linker. But not all of it is needed for your app to work.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pQSW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pQSW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png 424w, https://substackcdn.com/image/fetch/$s_!pQSW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png 848w, https://substackcdn.com/image/fetch/$s_!pQSW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png 1272w, https://substackcdn.com/image/fetch/$s_!pQSW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!pQSW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png" width="1456" height="912" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:912,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1838398,&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://blog.jacobstechtavern.com/i/139976243?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.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_!pQSW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png 424w, https://substackcdn.com/image/fetch/$s_!pQSW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png 848w, https://substackcdn.com/image/fetch/$s_!pQSW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png 1272w, https://substackcdn.com/image/fetch/$s_!pQSW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362dc469-30f7-43f6-b740-9eda3d62c876_1976x1238.png 1456w" sizes="auto, 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>
          <a href="https://blog.jacobstechtavern.com/p/app-size-101">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Advanced Agentic Engineering]]></title><description><![CDATA[Techniques developed while running several parallel agents every day]]></description><link>https://blog.jacobstechtavern.com/p/advanced-agentic-engineering</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/advanced-agentic-engineering</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 02 Sep 2026 15:02:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!WNK-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WNK-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WNK-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!WNK-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!WNK-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!WNK-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WNK-!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/adfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:2307018,&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://blog.jacobstechtavern.com/i/209096286?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WNK-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!WNK-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!WNK-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!WNK-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fadfe1be6-e78f-4dd6-8983-c7249f9d2d48_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>I am not here today to give you cryptic pointers about graphs and loops. I&#8217;ve got straightforward, battle-tested, practical tips you can implement today to squeeze the most out of your AI agents.</p><p>We no longer live in the coding flow-state. Today, it&#8217;s more akin to spinning plates. It&#8217;s just a different kind of flow.</p><p>Or, as this guy put it&#8230;</p><div class="twitter-embed" data-attrs="{&quot;url&quot;:&quot;https://x.com/contextconor/status/2079907141616324985?s=20&quot;,&quot;full_text&quot;:&quot;the era of autistic deep work is over\n\nyour agents are more autistic than you and better at deep work\n\nwelcome to the shallow work future\n\ntotal ADHD dominance&quot;,&quot;username&quot;:&quot;contextconor&quot;,&quot;name&quot;:&quot;conor brennan-burke&quot;,&quot;profile_image_url&quot;:&quot;https://pbs.substack.com/profile_images/2040111317445398528/5EBukbtu_normal.jpg&quot;,&quot;date&quot;:&quot;2026-07-22T12:31:20.000Z&quot;,&quot;photos&quot;:[],&quot;quoted_tweet&quot;:{},&quot;reply_count&quot;:251,&quot;retweet_count&quot;:816,&quot;like_count&quot;:12839,&quot;impression_count&quot;:405475,&quot;expanded_url&quot;:null,&quot;video_url&quot;:null,&quot;video_preview_media_key&quot;:null,&quot;belowTheFold&quot;:false}" data-component-name="Twitter2ToDOM"></div><p>I&#8217;ve been all-in on agentic engineering for more than a year now. And yes, that includes multiple months in 2025 on the deeply mediocre <em>early</em> Opus 4.x models. </p><p>This post is a combo of practical productivity tips and specific situational techniques I&#8217;ve picked up. There&#8217;s a clear through-line: What I&#8217;ve learned more than anything is that <strong>using these tools is a skill</strong>, and if you&#8217;re proficient with them, you aren&#8217;t getting replaced anytime soon. Some of these skills include:</p><ul><li><p>Delineating <em>&#8220;stuff they&#8217;re excellent at&#8221;</em> and <em>&#8220;stuff they are crap at&#8221;</em>.</p></li><li><p>Knowing how to kick off long-running work and run lots in parallel. </p></li><li><p>Ultimately, taste for what good software &amp; product looks like.</p></li></ul><div class="callout-block" data-callout="true"><p><em>Did you know I have a super generous referral programme? Get <strong>a free month of paid membership</strong> for each free subscriber you refer! </em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/leaderboard?&amp;utm_source=post&quot;,&quot;text&quot;:&quot;Refer a friend&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.jacobstechtavern.com/leaderboard?&amp;utm_source=post"><span>Refer a friend</span></a></p><p><em>I&#8217;m so close to 10k subscribers now, please help me over the line! &#128591;</em></p></div><div><hr></div><h2>Contents</h2><ol><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/never-lose-a-session">Never lose a session</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/how-far-can-you-go-with-verification">How far can you go with verification?</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/collaboration-and-argumentmd">Collaboration and ARGUMENT.md</a></p><ol><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/the-fast-lane">The Fast Lane</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/the-slow-lane">The Slow Lane</a></p></li></ol></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/design-exploration">Design Exploration</a> </p><ol><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/something-with-character">Something with Character</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/refining-the-aesthetic">Refining the Aesthetic</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/the-breakthrough">The Breakthrough</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/hitting-the-limits">Hitting the Limits</a></p></li></ol></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/unfamiliar-territory">Unfamiliar Territory</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/lots-of-personal-software">(Lots of) Personal Software</a></p><ol><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/tab-titles">Tab Titles</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/zsh-shortcuts">Zsh Shortcuts</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/openbundle">OpenBundle</a></p></li></ol></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/elevating-your-old-side-projects">Elevating your old side projects</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/building-at-a-higher-level-of-abstraction">Building at a higher level of abstraction</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/209096286/when-the-machines-take-over-ill-be-the-first-to-die">When the machines take over, I&#8217;ll be the first to die</a></p></li></ol><div><hr></div><p>This blog post is so full of knowledge that most email clients will cut it off. Please <a href="https://blog.jacobstechtavern.com/p/advanced-agentic-engineering">read on my website</a> for the best experience.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/p/advanced-agentic-engineering&quot;,&quot;text&quot;:&quot;Read on my website&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.jacobstechtavern.com/p/advanced-agentic-engineering"><span>Read on my website</span></a></p><div><hr></div><h2><strong>Never lose a session</strong></h2><p>This sort of belongs in <strong>&#8220;(Lots of) Personal Software&#8221;</strong>, but I&#8217;m mentioning it first, because nobody does this, and it&#8217;s my biggest timesaver.</p><p>Seriously, give it a try. </p><p>Everyone knows you can call <em>claude --resume</em> or <em>codex resume</em> (I never remember which one needs the dashes), but you ideally need to be in the exact folder from the original session: you can search <em>--all</em>, but you&#8217;re on your own if you have multiple checkouts, git worktrees, or ad-hoc agent sessions. </p><p>Even on the Codex &amp; Claude desktop apps, you need to know approximately where to look for. And god help you if you don&#8217;t recall which agent you used.</p><p>There&#8217;s a solution: <em>use your agents to track your agents. </em></p><p>I set up an automated ledger of all my agent sessions, including folder, transcript, and resume ID. An agent can semantically search the ledgers to find older sessions by just asking:  </p><blockquote><p><em>&#8220;Find me the agent session that ran the review-pass thing and show me how to get back into it.&#8221;</em></p><p><em>&#8230;</em></p><p><code>cd /Users/jacob/checkouts/1/granola/ios &amp;&amp; codex resume 019ecbcd-2d59-19e0-b8d1-a249622c98a5</code></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JJQV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JJQV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png 424w, https://substackcdn.com/image/fetch/$s_!JJQV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png 848w, https://substackcdn.com/image/fetch/$s_!JJQV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png 1272w, https://substackcdn.com/image/fetch/$s_!JJQV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!JJQV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png" width="1456" height="541" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b1a490c8-8c58-475e-a766-e1338816e703_1982x736.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:541,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:114147,&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://blog.jacobstechtavern.com/i/209096286?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.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_!JJQV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png 424w, https://substackcdn.com/image/fetch/$s_!JJQV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png 848w, https://substackcdn.com/image/fetch/$s_!JJQV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png 1272w, https://substackcdn.com/image/fetch/$s_!JJQV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1a490c8-8c58-475e-a766-e1338816e703_1982x736.png 1456w" sizes="auto, 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>You can do the same thing: just pass this prompt to an agent and it&#8217;ll just start working.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://gist.github.com/jacobsapps/5f3b7501a169085a3def5bb933a7b98d&quot;,&quot;text&quot;:&quot;Get the prompt on GitHub&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://gist.github.com/jacobsapps/5f3b7501a169085a3def5bb933a7b98d"><span>Get the prompt on GitHub</span></a></p><p><em>Most of the examples I demonstrate in this blog post were found using the above technique.</em></p><div><hr></div><h2><strong>How far can you go with verification?</strong></h2><p>Pretty damn far, honestly.</p><p>You should (hopefully)* already know that the most critical unlock with agentic engineering is the <em>verification loop</em>. That is, giving your agent the ability to check its own work via:</p><ul><li><p>building and running the code itself</p></li><li><p>checking visually against a design</p></li><li><p>unit tests</p></li></ul><p><em>*If this is news to you, please go ahead and <a href="https://blog.jacobstechtavern.com/p/agentic-ai-2026">read this free post</a> now. Come back after. I&#8217;ll wait.</em></p><p><strong>Automatic verification is the backbone of agent parallelism.</strong> Executing a plan and writing code might take a frontier model 5 minutes, maybe 10 for a whole screen. </p><p>But when you instruct them to check their work, that&#8217;s when you start to see 20 minute, hour-long, or even overnight working sessions. You can&#8217;t spin multiple plates unless your agents are checking their work. To maximise productivity, give your agent what it needs to execute and verify the work, then get out of its way and find another plate to toss on. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M9P4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M9P4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png 424w, https://substackcdn.com/image/fetch/$s_!M9P4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png 848w, https://substackcdn.com/image/fetch/$s_!M9P4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png 1272w, https://substackcdn.com/image/fetch/$s_!M9P4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!M9P4!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png" width="1200" height="530.7692307692307" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:644,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:361866,&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://blog.jacobstechtavern.com/i/209096286?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!M9P4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png 424w, https://substackcdn.com/image/fetch/$s_!M9P4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png 848w, https://substackcdn.com/image/fetch/$s_!M9P4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png 1272w, https://substackcdn.com/image/fetch/$s_!M9P4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7664ae9-11e0-429e-9382-57e0269cb9e2_2840x1256.png 1456w" sizes="auto, 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>As agents get better and better, I find myself surprised by the amount I can delegate.</p><p>It&#8217;s more art than science how hard you want to cognitively offload, but you can optimise by <em>trying to do as little verification work yourself</em>. It&#8217;s the lazy man&#8217;s paradise. The more you can delegate, the longer your agents can run, and the more parallel plates you can spin simultaneously.</p><p>My favourite technique with feature work, is asking the agent to <strong>take screenshots</strong> of all its work, and then <strong>host these in a local HTML page</strong>.</p><p>HTML is just aesthetically the nicest way to review feature &amp; design work. Here&#8217;s the verification page it created for the onboarding flow in my latest project, <strong><a href="https://apps.apple.com/app/the-damage-alcohol-tracker/id6795665746">The Damage</a></strong>, a casino-inspired drink-tracking app:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fWsr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fWsr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png 424w, https://substackcdn.com/image/fetch/$s_!fWsr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png 848w, https://substackcdn.com/image/fetch/$s_!fWsr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png 1272w, https://substackcdn.com/image/fetch/$s_!fWsr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!fWsr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png" width="1456" height="705" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:705,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4036389,&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://blog.jacobstechtavern.com/i/209096286?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.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_!fWsr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png 424w, https://substackcdn.com/image/fetch/$s_!fWsr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png 848w, https://substackcdn.com/image/fetch/$s_!fWsr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png 1272w, https://substackcdn.com/image/fetch/$s_!fWsr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba5d63f0-828f-4d87-a3f1-1078186765f0_2994x1450.png 1456w" sizes="auto, 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>I&#8217;ve gradually found ways to go a lot further:</p><ol><li><p>Put the Figma design on the left side, implementation on the right side.</p></li><li><p>Alternatively you can ask for a slider to check directly between before/after.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uHH5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uHH5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png 424w, https://substackcdn.com/image/fetch/$s_!uHH5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png 848w, https://substackcdn.com/image/fetch/$s_!uHH5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png 1272w, https://substackcdn.com/image/fetch/$s_!uHH5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!uHH5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png" width="1456" height="759" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:759,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3052168,&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://blog.jacobstechtavern.com/i/209096286?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.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_!uHH5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png 424w, https://substackcdn.com/image/fetch/$s_!uHH5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png 848w, https://substackcdn.com/image/fetch/$s_!uHH5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png 1272w, https://substackcdn.com/image/fetch/$s_!uHH5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faabce96d-b2b6-440e-8d6a-837335dcb832_2264x1180.png 1456w" sizes="auto, 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">Pictured: rebuilding my Metal-powered Mandelbrot set</figcaption></figure></div></li><li><p>Ask the agent to take a screen recording; especially nice for animation work.</p></li><li><p>If you&#8217;re trying to understand something, one of my all-time favourite techniques is asking the agent to generate an <a href="https://excalidraw.com/">excalidraw diagram</a>.</p></li><li><p>Often I&#8217;ll tell my Sol agent <em>&#8220;when you&#8217;ve finished work, spin up a Fable agent and ask for a code review, and fix anything that doesn&#8217;t result in needlessly defensive slop&#8221;</em>. Or vice versa if I&#8217;m using Fable. The &#8220;needlessly defensive&#8221; line is important, since an agent will <em>always</em> find 20 things, most of which are bloat.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e93x!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e93x!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png 424w, https://substackcdn.com/image/fetch/$s_!e93x!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png 848w, https://substackcdn.com/image/fetch/$s_!e93x!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png 1272w, https://substackcdn.com/image/fetch/$s_!e93x!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!e93x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png" width="1456" height="952" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:952,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:431901,&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://blog.jacobstechtavern.com/i/209096286?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.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_!e93x!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png 424w, https://substackcdn.com/image/fetch/$s_!e93x!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png 848w, https://substackcdn.com/image/fetch/$s_!e93x!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png 1272w, https://substackcdn.com/image/fetch/$s_!e93x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd90700d2-a979-4d55-a112-51c1aaa60fbc_2400x1570.png 1456w" sizes="auto, 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">From some unreleased reverse-engineering research I did on Springboard and launchd</figcaption></figure></div><ol start="5"><li><p>For my pre-release polish pass, I requested an almanac of <strong>every screen</strong> in The Damage, hosted on a tiny local server, with a text box each. <br>I gave detailed visual feedback on each screen individually, which was automatically linked to the specific screen. <br>Sol-xhigh worked autonomously through the night (the first pass took 5 hours) to implement everything ready for App Review.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!A5Uz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!A5Uz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png 424w, https://substackcdn.com/image/fetch/$s_!A5Uz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png 848w, https://substackcdn.com/image/fetch/$s_!A5Uz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png 1272w, https://substackcdn.com/image/fetch/$s_!A5Uz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!A5Uz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png" width="1456" height="798" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:798,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1487671,&quot;alt&quot;:&quot;&quot;,&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://blog.jacobstechtavern.com/i/209096286?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!A5Uz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png 424w, https://substackcdn.com/image/fetch/$s_!A5Uz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png 848w, https://substackcdn.com/image/fetch/$s_!A5Uz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png 1272w, https://substackcdn.com/image/fetch/$s_!A5Uz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e7779ef-9999-477b-a3cf-d43c3efd464b_2440x1338.png 1456w" sizes="auto, 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>At this point, you can go pretty far without opening your app a single time.</p><p>If you&#8217;re doing things right, you&#8217;ll perhaps find yourself in a weird purgatorial zone at ~3pm: you&#8217;ve maxed out on 10ish terminal tabs, all of which are running without you.</p><div><hr></div><h2><strong>Collaboration and ARGUMENT.md</strong></h2><p>It&#8217;s an inevitability in engineering that you often run into an <a href="https://blog.jacobstechtavern.com/p/the-most-complex-system">insanely complex problem</a> or incomprehensible <a href="https://blog.jacobstechtavern.com/p/hardcore-debugging">mystery bug</a>. Asking an agent to help just won&#8217;t cut it, especially if you&#8217;re rushing to solve an urgent bug in production.</p><h4>The Fast Lane</h4><p>When you really need to pull out the big guns, here is my secret weapon:</p><div class="callout-block" data-callout="true"><p>Paid members find out about this secret weapon, as well as getting the subsequent 5 chapters, plus my new comedy eBook. And, of course:</p><p><span>&#9875;&#65039; Access my </span><a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 50+ paywalled articles</a><span> (including this one)</span><br><span>&#128640; Read </span><a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a><span> a month before anyone else</span><br><span>&#129525; Master concurrency with my </span><a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a><span> and </span><a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br><span>&#129489;&#8205;&#128640; Get a free copy of my new eBook, &#8220;</span><a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a><span>&#8221;</span><br><span>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=209096286&quot;,&quot;text&quot;:&quot;Get 14 day free trial&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=209096286"><span>Get 14 day free trial</span></a></p></div>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/advanced-agentic-engineering">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Apple Watch brings distributed system headaches to your app]]></title><description><![CDATA[&#8220;My Toddler Loves Planes, So I Built Him a Distributed System&#8221;]]></description><link>https://blog.jacobstechtavern.com/p/apple-watch-distributed-systems</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/apple-watch-distributed-systems</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 19 Aug 2026 15:02:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NpmE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NpmE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NpmE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!NpmE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!NpmE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!NpmE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NpmE!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:3046399,&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://blog.jacobstechtavern.com/i/205950982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NpmE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!NpmE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!NpmE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!NpmE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde277e08-adcf-4ecb-a6f2-1719c60a16ed_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>The best day of a man&#8217;s life is the day he gets his shiny new Apple Watch MK Ultra. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IYWj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IYWj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic 424w, https://substackcdn.com/image/fetch/$s_!IYWj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic 848w, https://substackcdn.com/image/fetch/$s_!IYWj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic 1272w, https://substackcdn.com/image/fetch/$s_!IYWj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IYWj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic" width="1456" height="1092" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1349725,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/heic&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/205950982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IYWj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic 424w, https://substackcdn.com/image/fetch/$s_!IYWj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic 848w, https://substackcdn.com/image/fetch/$s_!IYWj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic 1272w, https://substackcdn.com/image/fetch/$s_!IYWj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F281e61ad-3b57-48ee-9e85-d7eb40b5bf59.heic 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><p>The worst day of a man&#8217;s life is the day he tries to develop for it. </p><p>This is my story.</p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/205950982/watch-woes">Watch Woes</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/205950982/aviator-v30">Aviator v3.0</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/205950982/distributed-systems">Distributed Systems</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/205950982/cap-theorem">CAP Theorem</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/205950982/wcsession-apis">WCSession APIs</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/205950982/designing-our-link">Designing our Link</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/205950982/more-complex-systems">A More Complex System</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/205950982/last-orders">Last Orders</a></p></li></ul><div><hr></div><h2>Watch Woes</h2><p>Apple does not pull any punches when it comes to watch devex. You can&#8217;t just connect a cable, it only works via Wi-Fi. Specifically, it only works if you happen to have 2.4 GHz Wi-Fi. Even so, Xcode probably won&#8217;t notice your watch for several hours. A&#823;n&#821;d&#820; w&#822;h&#823;e&#824;n&#820; i&#821;t&#823; d&#822;o&#821;e&#823;s&#824;, i&#820;t&#821; w&#822;i&#823;l&#820;l&#821; t&#822;a&#824;k&#821;e&#822; a&#821;n&#823;o&#824;t&#820;h&#821;e&#822;r&#823; h&#821;o&#824;u&#822;r&#823; t&#820;o&#821; c&#822;o&#823;p&#824;y&#820; c&#821;a&#822;c&#823;h&#824;e&#820; s&#822;y&#823;m&#821;b&#824;o&#820;l&#822;s&#823;. W&#820;a&#822;n&#823;t&#824; t&#821;o&#823; g&#822;e&#824;t&#821; a&#822; t&#823;e&#821;a&#824;m&#822;m&#823;a&#820;t&#822;e&#824;&#8217;&#821;s&#823; U&#822;U&#824;I&#821;D&#823; f&#824;o&#822;r&#823; a&#821;d&#824;-&#823;h&#822;o&#821;c&#823; b&#824;u&#820;i&#823;l&#822;d&#824;s&#821;? G&#823;o&#824;o&#821;d&#822; l&#823;u&#824;c&#821;k&#822;: y&#823;o&#824;u&#821; h&#823;a&#822;v&#824;e&#821; t&#822;o&#823; g&#824;o&#821; t&#823;h&#822;r&#824;o&#821;u&#823;g&#822;h&#824; t&#821;h&#823;i&#822;s&#824; w&#821;h&#823;o&#822;l&#824;e&#821; r&#823;i&#822;g&#824;m&#823;a&#822;r&#824;o&#821;l&#823;e&#822; a&#824;g&#821;a&#823;i&#822;n&#824;. I&#821;&#846;f&#823;&#816; y&#822;&#797;o&#824;&#839;u&#823;&#804; h&#820;&#853;a&#823;&#827;v&#824;&#852;e&#822;&#810; a&#821;&#841; S&#823;&#799;E&#822;&#798;? A&#824;&#790;l&#823;&#800;l&#822;&#812; t&#821;&#791;h&#824;&#857;e&#823;&#853;s&#822;&#851;e&#821;&#809; p&#823;&#817;r&#824;&#805;o&#820;&#792;b&#823;&#797;l&#822;&#852;e&#821;&#793;m&#824;&#798;s&#823;&#826; a&#822;&#817;r&#824;&#811;e&#823;&#840; 1&#820;&#845;0&#824;&#818;x&#822;&#805; w&#823;&#792;o&#822;&#846;r&#824;&#806;s&#821;&#858;e&#823;&#809;. O&#821;&#810;&#787;h&#823;&#812;&#850;,&#824;&#841;&#772; a&#822;&#827;&#829;n&#823;&#814;&#859;d&#820;&#798;&#861; <a href="https://developer.apple.com/forums/thread/821989">T&#823;&#825;&#830;e&#821;&#793;&#844;s&#824;&#806;&#785;t&#822;&#797;&#787;F&#823;&#793;&#781;l&#820;&#791;&#838;i&#821;&#796;&#861;g&#823;&#858;&#786;h&#822;&#819;&#859;t&#821;&#854;&#788; w&#823;&#845;&#781;a&#821;&#811;&#830;s&#824;&#790;&#786; p&#823;&#812;&#831;a&#822;&#858;&#842;r&#824;&#857;&#855;t&#821;&#809;&#864;i&#823;&#796;&#831;a&#822;&#858;&#842;l&#823;&#857;&#855;l&#821;&#809;&#864;y&#824;&#812;&#843; b&#822;&#852;&#861;r&#823;&#818;&#783;o&#820;&#816;&#848;k&#822;&#826;&#784;e&#823;&#811;&#849;n&#820;&#825;&#829; f&#823;&#793;&#838;o&#821;&#819;&#864;r&#820;&#799;&#855; s&#821;&#801;&#812;&#784;e&#822;&#808;&#826;&#861;v&#821;&#809;&#853;&#842;e&#823;&#810;&#797;&#829;r&#820;&#791;&#846;&#781;a&#824;&#806;&#804;&#783;l&#823;&#802;&#857;&#843; m&#821;&#812;&#825;&#834;o&#823;&#814;&#841;&#788;n&#820;&#839;&#806;&#786;t&#820;&#795;&#818;&#849;h&#821;&#839;&#828;&#861;s&#823;&#808;&#812;&#855;</a> o&#820;&#816;&#828;&#781;f&#824;&#807;&#790;&#810;&#781; t&#821;&#795;&#858;&#846;&#776;h&#822;&#801;&#804;&#819;&#829;i&#822;&#802;&#815;&#852;&#775;s&#821;&#791;&#857;&#848; y&#824;&#801;&#858;&#792;&#776;&#769;e&#823;&#808;&#814;&#792;&#830;a&#822;&#839;&#858;&#854;&#844;r&#822;&#802;&#791;&#826;&#859;.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><p>Nevertheless, one speeds ahead. Probing one&#8217;s side projects for an excuse to make the most of one&#8217;s new toy. The good news: my one-and-a-half-year-old toddler has began getting really into planes when he sees them in the sky. We&#8217;re ready for round 2!</p><div><hr></div><h5>Sponsored Link</h5><blockquote><h4><a href="https://shipaton.com/?utm_medium=sponsored&amp;utm_source=JacobsTechTavern&amp;utm_campaign=newsletter&amp;utm_content=shipaton">The world&#8217;s biggest mobile hackathon for people who actually ship</a></h4><p>Shipaton 2026 is RevenueCat&#8217;s global hackathon for builders launching real apps to real stores, competing for over $1M in prizes, and showing the world what they shipped. Build, launch, and submit your app by September 30. Check it out <a href="https://shipaton.com/?utm_medium=sponsored&amp;utm_source=JacobsTechTavern&amp;utm_campaign=newsletter&amp;utm_content=shipaton">here</a>.</p></blockquote><div><hr></div><h2>Aviator v3.0</h2><p>For anyone unfamiliar with <a href="https://news.ycombinator.com/item?id=38435908">my most popular article ever</a>, I built a skeuomorphic radar toy using SwiftUI, shaders, and open-source APIs to help locate nearby planes in the sky. </p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;b9626880-a8ea-4e1e-bf9d-7c5c4deb64d1&quot;,&quot;caption&quot;:&quot;This is a tale of inspiration, of a vision taking shape, and of rapid feedback from that most discerning of customers: a 2-year-old girl. Keen to skip ahead to the final product? Go ahead and download Aviator &#8212; Radar on your Phone from the App Store now!&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;md&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;My Toddler Loves Planes, So I Built Her A Radar&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Master iOS. Boost your salary. Join 100,000 senior Swift devs learning advanced concurrency, SwiftUI, and iOS performance for 10 minutes a week. Sign up free today!&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2023-11-27T18:00:26.285Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!4TFQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee63ddba-ea10-4c84-8364-da190324f103_1680x1200.png&quot;,&quot;cover_image_alt&quot;:null,&quot;cover_image_crop&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/my-toddler-loves-planes-so-i-built&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:139111344,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:88,&quot;comment_count&quot;:48,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>I re-implemented my radar as a Watch app! </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PaS_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PaS_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png 424w, https://substackcdn.com/image/fetch/$s_!PaS_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png 848w, https://substackcdn.com/image/fetch/$s_!PaS_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png 1272w, https://substackcdn.com/image/fetch/$s_!PaS_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!PaS_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png" width="1314" height="514" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:514,&quot;width&quot;:1314,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:356980,&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://blog.jacobstechtavern.com/i/205950982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.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_!PaS_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png 424w, https://substackcdn.com/image/fetch/$s_!PaS_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png 848w, https://substackcdn.com/image/fetch/$s_!PaS_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png 1272w, https://substackcdn.com/image/fetch/$s_!PaS_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6a4c1b8-9308-42ba-9b24-61c9f85c6a40_1314x514.png 1456w" sizes="auto, 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>This is a really cool miniature version of the full app, itself a neat SwiftUI wrapper over the <a href="https://opensky-network.org/data/api">OpenSky API</a>.</p><p>But it doesn&#8217;t feel like an <em>extension</em> of my app. It feels like a little miniature version that runs totally separately. Because it is. This won&#8217;t please my dozen users. They have high expectations of me, for some reason. So let&#8217;s go back to basics. </p><p>At the <em>very least</em>, we should make sure the settings are shared between our two targets. This includes: </p><p>&#128506;&#65039; The map/radar toggle  <br>&#127468;&#127463; The show/hide flags toggle  <br>&#128269; The zoom control<br>&#127912; The colour selector </p><p>So we need our watch and our phone to talk. Like this:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;868f8e5b-c51f-43ed-b5cb-38e8eb9ae33c&quot;,&quot;duration&quot;:null}"></div><p>But, like socially awkward British men <em>(redundant phrasing)</em>, getting the two devices to communicate isn&#8217;t trivial. </p><p>iPhone and Apple Watch are separate systems: two small computers with their own file systems, their own secure enclaves, their own cell radios, and their own separate apps. These twin targets form a <strong>distributed system</strong>.</p><p>Each device is a <em>writer</em> <em>node</em>, both storing and mutating a replica of user settings.  </p><p>Even the na&#239;ve approach introduces thorny state management problems. What happens to the data if my toddler and I modify the same settings on the phone and watch simultaneously? What happens to this data if they can&#8217;t connect? What happens when they do reconnect? What if the watch runs out of power, this reconnection takes a week, and an ancient settings change later propagates? Should our settings change happen instantly on each client or do we wait until the data is shared?</p><p>The mind boggles. </p><p>How am I, a lowly frontend developer, supposed to handle this?! </p><p>I threw my MacBook across the room in a fury. </p><p>And gasped.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jWRj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jWRj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png 424w, https://substackcdn.com/image/fetch/$s_!jWRj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png 848w, https://substackcdn.com/image/fetch/$s_!jWRj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png 1272w, https://substackcdn.com/image/fetch/$s_!jWRj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!jWRj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png" width="1448" height="1086" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1086,&quot;width&quot;:1448,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1933084,&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://blog.jacobstechtavern.com/i/205950982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.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_!jWRj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png 424w, https://substackcdn.com/image/fetch/$s_!jWRj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png 848w, https://substackcdn.com/image/fetch/$s_!jWRj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png 1272w, https://substackcdn.com/image/fetch/$s_!jWRj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6248ebcb-777a-459b-aea0-1eb04445c358_1448x1086.png 1456w" sizes="auto, 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>The answer was right in front of me the whole time. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2><strong>Distributed Systems</strong></h2><p>Before they invented AI engineering, distributed systems engineering was probably the highest-status, most hardcore-sounding type of engineering out there.</p><blockquote><p><em>(Us greybeards on mobile are a little over the hump since the glory days of the early 2010s).</em></p></blockquote><p>It&#8217;s considered hard because, unlike building CRUD APIs, writing speedy single-node database queries, painting JSON, or setting up clean state management, there is often no simple solution to a problem.</p><div><hr></div><h4>CAP Theorem </h4><p>The linchpin of distributed systems theory, that is, the root cause of our headaches, is CAP theorem. For a distributed system, that is, <em>a set of nodes that store data</em>, CAP theorem states:</p><div class="callout-block" data-callout="true"><p><em>&#8220;Consistency. Availability. Partition Tolerance. Pick 2 out of 3.&#8221;</em></p><p><em>&#8212; Eric Brewer (paraphrased), 2000 </em></p></div><p><strong>Consistency</strong> = every node always has the same data. </p><p><strong>Availability</strong> = every request to a node must always give a response. </p><p><strong>Partition Tolerance</strong> = the system still works when nodes can&#8217;t communicate. </p><p>To achieve <strong>Consistency</strong>, all data mutation operations must be immediately applied to all nodes in the system before they return. For <strong>Availability</strong>, you need to give a prompt response to all valid operations, even if part of the system is down.</p><p>With distributed systems, partitions are <strong>inevitable</strong>. So partition tolerance is non-negotiable. This leaves us mucking about with the other 2. In the context of the<em> </em>Watch/Phone system, a partition might happen when Bluetooth is off, your watch ran out of battery, or you just left it on the train <em>(f***!)</em>.</p><p>When this partition happens, you have to choose: </p><ul><li><p>Compromise on availability: throw errors because we can&#8217;t ensure consistency across all nodes.</p></li><li><p>Provide availability at the expense of consistency by giving a best-effort response to operations, and syncing data across nodes when we can.</p></li></ul><p>Recall my somewhat meandering descent into madness in the last chapter. Can you see how we can solve most of the problems by making a choice between consistency and availability?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SIg8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SIg8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png 424w, https://substackcdn.com/image/fetch/$s_!SIg8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png 848w, https://substackcdn.com/image/fetch/$s_!SIg8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png 1272w, https://substackcdn.com/image/fetch/$s_!SIg8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!SIg8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png" width="1456" height="937" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:937,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:609187,&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://blog.jacobstechtavern.com/i/205950982?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.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_!SIg8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png 424w, https://substackcdn.com/image/fetch/$s_!SIg8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png 848w, https://substackcdn.com/image/fetch/$s_!SIg8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png 1272w, https://substackcdn.com/image/fetch/$s_!SIg8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76eeda2-01fd-4942-a3bb-1f66f5a22427_3446x2217.png 1456w" sizes="auto, 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>This basic theory forms the foundation of distributed systems engineering. And now we know enough to design our twin radar apps. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2><strong>WCSession APIs</strong></h2><p>Apple created the <a href="https://developer.apple.com/documentation/watchconnectivity">Watch Connectivity</a> framework to allow our devices to speak to each other. The system handles a lot of legwork, taking responsibility for data transfer across whatever medium (Bluetooth, Wi-Fi, cellular). Data transfer can be done in real time, or in the background if the app is inactive. </p><p>There are just a few <a href="https://developer.apple.com/documentation/watchconnectivity/wcsession">WCSession APIs</a> to choose from when transferring our data, and both nodes, phone and watch, can implement them, enabling bidirectional communication.</p><p><strong><a href="https://developer.apple.com/documentation/watchconnectivity/wcsession/sendmessage(_:replyhandler:errorhandler:)">sendMessage()</a></strong> sends a dictionary of values, with closures for both replies and errors. This means you can use it to both send <em>and</em> request data, but it fails quickly if there&#8217;s a partition <em>(i.e. no connection)</em>, as it needs a live counterpart to succeed. </p><p>This API allows us to achieve consistency if we need it, but not availability.</p><div><hr></div><p><strong><a href="https://developer.apple.com/documentation/watchconnectivity/wcsession/updateapplicationcontext(_:)">updateApplicationContext()</a></strong> also transmits a dictionary of values, called applicationContext, shared between the two nodes. The receiving node implements <strong>session(_:didReceiveApplicationContext:)</strong> to retrieve the latest data. </p><p>applicationContext is always going to have the most recent edit we made: treat it like a last-write-wins strategy. You can fire-and-forget this method, since it works even during a partition, delivering what it can once it&#8217;s connected.</p><p>This API offers us good availability and <strong>eventual consistency</strong>.</p>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/apple-watch-distributed-systems">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[The Hardest Problems in Mobile Engineering. ]]></title><description><![CDATA[10 reasons why your life sucks]]></description><link>https://blog.jacobstechtavern.com/p/10-hardest-problems-in-mobile</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/10-hardest-problems-in-mobile</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Tue, 11 Aug 2026 15:02:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-ak3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-ak3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-ak3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!-ak3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!-ak3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!-ak3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-ak3!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:5583639,&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://blog.jacobstechtavern.com/i/194188885?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-ak3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!-ak3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!-ak3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!-ak3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18c87fdb-d527-4c1c-95d6-2e66ee3acd18_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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">Oh, naughty, you&#8217;ve mixed Aztec and Greek myth, I hope you don&#8217;t get an interdenominational&#8230; erm, you know, from mixing the two art styles&#8230; a hangover of that kind.</figcaption></figure></div><p>I try to avoid gushing on this blog, it&#8217;s not my brand, but the iPhone keynote in 2007 was probably the best corporate presentation of all time. </p><p>Jobs introduced the iPhone featuring multi-touch, multi-tasking, networking, power management, security features, graphics frameworks, motion sensors, a camera, and &#8220;desktop-class&#8221; email &amp; web browsing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RTqq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RTqq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RTqq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg" width="1280" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:37049,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/197332009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!RTqq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.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><figcaption class="image-caption">I wrote about this a lot <a href="https://blog.jacobstechtavern.com/publish/post/197332009?back=%2Fpublish%2Fposts%2Fscheduled">here</a></figcaption></figure></div><p>&#8220;iPhone runs OS X&#8221; was regarded as a historic technical achievement <em>(and a very handy marketing shortcut)</em>. In a decade defined by miniaturisation of technology products, Apple found a way to run computer-grade software into tiny battery-powered devices, converting Blackberries into antiques in one fell swoop. </p><p>How far we&#8217;ve come since 2007. </p><p>AI labs are busy invoking the machine god, while I, a moderately prolific mobile engineer, am having daily heated arguments about <a href="https://x.com/jacobtechtavern/status/1849103488543805828">how to launch quickly</a> or <a href="https://x.com/jacobtechtavern/status/2033906662805823750">how to scroll</a> without hitting an out-of-memory exception.</p><p>Uh. Anyway.</p><p>If you work in mobile, here&#8217;s 10 reasons why your life sucks:</p><ol><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/1-modular-architecture-that-isnt-spaghetti">Modular Architecture (that isn&#8217;t spaghetti)</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/2-oh-you-want-to-release">Oh, you want to release?</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/3-architecture-astronauts-and-sync-engines">Architecture Astronauts &amp; Sync Engines</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/4-you-are-the-oss-btch">You are the OS&#8217;s b*tch</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/5-a-phone-is-a-coffin">A phone is a coffin</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/6-begging-your-users-for-core-functionality">Begging your users for core functionality</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/7-multiplatform-motherfckery">Multiplatform motherf*ckery</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/8-your-build-system-sucks-or-does-it">Your build system sucks (or does it?)</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/9-peripherals-and-distributed-systems">Peripherals &amp; Distributed Systems</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/194188885/10-nobody-downloads-apps-anymore">Nobody downloads apps anymore</a></p></li></ol><div><hr></div><p>This post is quite long, so your email client may cut it off. <a href="https://blog.jacobstechtavern.com/p/10-hardest-problems-in-mobile">Read on my website</a> for the best experience. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/p/10-hardest-problems-in-mobile&quot;,&quot;text&quot;:&quot;Read on my website&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://blog.jacobstechtavern.com/p/10-hardest-problems-in-mobile"><span>Read on my website</span></a></p><div><hr></div><h3>1. Modular Architecture (that isn&#8217;t spaghetti) &#127837;</h3><p>I always find it delightfully droll when people argue about screen-level architecture.</p><p>It&#8217;s not hard guys.</p><p>Pick an architecture, keep it consistent, bada-bing, bada-boom. Now anyone can onboard to your codebase without getting disoriented between screens. It&#8217;s a dull argument, and I&#8217;m tired.*</p><blockquote><p><em>*That VIPER thought, amirite?</em></p></blockquote><p>The architecture that matters, the one where you actually need to make interesting choices, is laying out your modules in the wider dependency graph of your project. And keeping it tidy.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qobc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qobc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp 424w, https://substackcdn.com/image/fetch/$s_!qobc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp 848w, https://substackcdn.com/image/fetch/$s_!qobc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp 1272w, https://substackcdn.com/image/fetch/$s_!qobc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!qobc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp" width="1400" height="765" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7063780a-33d3-40db-9248-6119838736ff_1400x765.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:765,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:252898,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/194188885?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qobc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp 424w, https://substackcdn.com/image/fetch/$s_!qobc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp 848w, https://substackcdn.com/image/fetch/$s_!qobc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp 1272w, https://substackcdn.com/image/fetch/$s_!qobc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7063780a-33d3-40db-9248-6119838736ff_1400x765.webp 1456w" sizes="auto, 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">From <a href="https://medium.com/better-programming/visualizing-dependency-graph-in-project-71210a5de269">Visualize Your iOS App&#8217;s Dependency Graph</a> by Ashwin Shrestha</figcaption></figure></div><p>Your modular architecture really solves an organisational problem: <em>how can lots of people work on this project without stepping all over each other?</em> Modules make it trivial to carve out ownership boundaries. </p><p>Smaller teams will probably still want to modularise. With tools like SPM or Tuist, it&#8217;s extremely easy to do so these days. Thoughtfully splitting your app into modules brings build time improvements and, more importantly, <em>sparks joy</em> when you look at your lovely unidirectional dependency graph. </p><p>When designing Granola&#8217;s modular architecture, I took a lot of inspiration from <a href="https://tuist.dev/en/docs/guides/features/projects/tma-architecture">Tuist&#8217;s TMA</a> (The Modular Architecture) and <a href="https://blog.jacobstechtavern.com/p/revenuecat-sdk">RevenueCat&#8217;s layered SDK</a>. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KdCi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KdCi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png 424w, https://substackcdn.com/image/fetch/$s_!KdCi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png 848w, https://substackcdn.com/image/fetch/$s_!KdCi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png 1272w, https://substackcdn.com/image/fetch/$s_!KdCi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!KdCi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png" width="1456" height="986" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:986,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:360814,&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://blog.jacobstechtavern.com/i/194188885?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.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_!KdCi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png 424w, https://substackcdn.com/image/fetch/$s_!KdCi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png 848w, https://substackcdn.com/image/fetch/$s_!KdCi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png 1272w, https://substackcdn.com/image/fetch/$s_!KdCi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c2f2be0-dece-412d-9cb0-e035f219233b_1920x1300.png 1456w" sizes="auto, 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>As we ascend the agentic engineering abstraction ladder and get further from the code, keeping on top of system design is more and more critical. </p><div><hr></div><h3>2. Oh, you want to release?</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bnVC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bnVC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif 424w, https://substackcdn.com/image/fetch/$s_!bnVC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif 848w, https://substackcdn.com/image/fetch/$s_!bnVC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif 1272w, https://substackcdn.com/image/fetch/$s_!bnVC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!bnVC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif" width="480" height="270" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:270,&quot;width&quot;:480,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1466691,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/194188885?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bnVC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif 424w, https://substackcdn.com/image/fetch/$s_!bnVC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif 848w, https://substackcdn.com/image/fetch/$s_!bnVC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif 1272w, https://substackcdn.com/image/fetch/$s_!bnVC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F100242d7-c96e-4fda-8fa9-77ee9f991c43_480x270.gif 1456w" sizes="auto, 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>Apple saw what happened to Microsoft in the 2000s. </p><p>Slowly, the impenetrable walled garden of their OS monopoly cracked and leaked as developers flooded to the freedom of the open web. Google ate their lunch across the successive tech cycles of web <em>and</em> mobile. Apple said, <em>not I</em>. </p><p>Distribution is <strong>everything</strong>. It&#8217;s the core reason Jobs pivoted from &#8220;HTML5 web apps are great for building on iOS!&#8221; in 2007 to the App Store in 2008. Devs who want to ship apps can&#8217;t get around Apple (<a href="https://blog.jacobstechtavern.com/p/jailbreak-enemies-with-a-link-remote-execution">except our friends at NSO Group</a>), they need to ask permission every time they release. </p><p>App Review is ostensibly there for consumer protection: keeping a wash of slop off the Store. But we know it&#8217;s there to remind us who&#8217;s boss. We live and work under the imperious thumb of the ghost of god-emperor Steve Jobs. Or perhaps his middle finger.</p><p>This is why mobile teams usually skew a little older. We&#8217;re not really. We&#8217;re 30. We just lost all our hair because of App Review stress. Oh, to be able to ship in 30 seconds like our friends on the web. I hope you remembered feature flags.</p><div><hr></div><p><em>Fun last-minute update to this section: this week I got possibly the most infuriating app rejection ever for my new drink-tracking app, &#8220;<a href="https://apps.apple.com/app/the-damage/id6795665746">The Damage</a>&#8221;</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!j-zC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!j-zC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png 424w, https://substackcdn.com/image/fetch/$s_!j-zC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png 848w, https://substackcdn.com/image/fetch/$s_!j-zC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png 1272w, https://substackcdn.com/image/fetch/$s_!j-zC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!j-zC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png" width="1456" height="589" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/05383222-74ec-4c78-9848-38d365d71766_2906x1176.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:589,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3157132,&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://blog.jacobstechtavern.com/i/194188885?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.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_!j-zC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png 424w, https://substackcdn.com/image/fetch/$s_!j-zC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png 848w, https://substackcdn.com/image/fetch/$s_!j-zC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png 1272w, https://substackcdn.com/image/fetch/$s_!j-zC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05383222-74ec-4c78-9848-38d365d71766_2906x1176.png 1456w" sizes="auto, 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>Instead of appreciating my genius custom tab bar for what it is, I got summarily rejected under &#8220;Spam&#8221; for building &#8220;primarily a drinking game app&#8221;, and they suggested I build a WEB APP instead <span>&#129318;&#8205;&#9794;&#65039;</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_!7Nfp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7Nfp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png 424w, https://substackcdn.com/image/fetch/$s_!7Nfp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png 848w, https://substackcdn.com/image/fetch/$s_!7Nfp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!7Nfp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7Nfp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png" width="1456" height="929" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:929,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:320989,&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://blog.jacobstechtavern.com/i/194188885?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.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_!7Nfp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png 424w, https://substackcdn.com/image/fetch/$s_!7Nfp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png 848w, https://substackcdn.com/image/fetch/$s_!7Nfp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!7Nfp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32728faa-adcc-49f8-8c2f-566958bd7068_2382x1520.png 1456w" sizes="auto, 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>Unfortunately, Apple failed to understand how autistic I get when I go on a warpath: I responded with a 3-pronged attack of replying, appealing to the App Review Board, and emailing a couple of my friends in DevRel to help correct the injustice. I got approved. </p><div><hr></div><p>Oh, and this wasn't even <em>close</em> to my worst run-in with App Review: it nearly got me fired back in 2023. I worked for a startup building a consent-oriented data ingestion pipeline, where we got stonewalled and blocked from releasing for over 6 months.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;8f1d2278-a0bd-472a-9a46-155e62fd70c8&quot;,&quot;caption&quot;:&quot;I&#8217;ve had a combative relationship with Apple in my writing career. Frankly, as the richest company in the world (erm, I think they&#8217;re still up there), they can probably cope with me taking a few potshots.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Apple Tried To Kill My Startup&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Master iOS. Boost your salary. Join 100,000 senior Swift devs learning advanced concurrency, SwiftUI, and iOS performance for 10 minutes a week. Sign up free today!&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2025-05-13T15:00:33.309Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!z5R2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc2dc49b-9f70-4852-a0a0-be4d37b728e3_1680x1200.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/apple-tried-to-kill-my-startup&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:156383349,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:13,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><h3>3. <strong>Architecture Astronauts &amp; Sync Engines</strong></h3><p><span>There comes a day in the life of every architecture astronaut where he (and yes it usually is a </span>he<span>) wakes up in a cold sweat: &#8220;OUR DATA. IT&#8217;S NOT ROBUST ENOUGH!&#8221;</span></p><p>Rapidly followed by a 16-hour Monster Energy Mango Loco&#8482; binge, and subsequent 12,000 line PR (before it was cool), which lands without review and single-handedly introduces a jobs program to rival the New Deal, except the deal is that you fix a shitload of weird concurrency queue and database edge cases.</p><div class="callout-block" data-callout="true"><p>The subsequent 7-and-a-half reasons are behind a paywall <em>(and intentionally slightly obscure contents headers)</em>. If you want 75ish% more value from my blog, you know what to do. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=194188885&quot;,&quot;text&quot;:&quot;Get 14 day free trial&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=194188885"><span>Get 14 day free trial</span></a></p><p>Paid members get lots of treats to help forget the pain of mobile engineering:</p><p>&#9875;&#65039; Access my <a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 50 paywalled articles</a> (including this one)<br>&#128640; Read <a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a> a month before anyone else<br>&#129525; Master concurrency with my <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a> and <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br>&#129489;&#8205;&#128640; Get a free copy of my new eBook, &#8220;<a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a>&#8221;<br>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</p></div>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/10-hardest-problems-in-mobile">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Introducing... The Tech Tavern Roadmap 🗺️]]></title><description><![CDATA[Level up your skills in your chosen topics]]></description><link>https://blog.jacobstechtavern.com/p/introducing-the-tech-tavern-roadmap</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/introducing-the-tech-tavern-roadmap</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Tue, 04 Aug 2026 15:03:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!dCXh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I&#8217;ve published almost every week for over 3 years. </p><p>A lot of bangers. And a few duds.</p><p>With an overwhelming back-catalog <em>(193 posts and counting!)</em>, how can I expect you to sift out the gold?</p><p><strong>You no longer have to.</strong></p><p>Over the last few months, I&#8217;ve been working on a roadmap to help you get to grips with my past work, pick a focus, and quickly level up your knowledge. A curated menu of articles targeted at each experience level.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://roadmap.jacobstechtavern.com&quot;,&quot;text&quot;:&quot;View the roadmap here&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://roadmap.jacobstechtavern.com"><span>View the roadmap here</span></a></p><p>I aim at 3 levels, but you can start anywhere you like: if you want to sharpen your knowledge of any topic, you now have a recommended start point.</p><h3><strong><a href="https://roadmap.jacobstechtavern.com/?level=foundations">&#128035; Foundations</a></strong></h3><p>For devs looking to understand iOS fundamentals, hoping to break into a career, or build up a portfolio of side projects.</p><ul><li><p><a href="https://roadmap.jacobstechtavern.com/?level=foundations&amp;topic=swiftui-side-projects">SwiftUI &amp; Side Projects</a></p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=foundations&amp;topic=concurrency-fundamentals">Concurrency Fundamentals</a></p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=foundations&amp;topic=ios-job-interviews">iOS Job Interviews</a></p></li></ul><h3><strong><a href="https://roadmap.jacobstechtavern.com/?level=levelling-up">&#128640; Levelling Up</a></strong></h3><p>For seasoned engineers shipping features, who want to sharpen the instincts and knowledge base that define the best senior engineers.</p><ul><li><p><a href="https://roadmap.jacobstechtavern.com/?level=levelling-up&amp;topic=concurrency">Concurrency</a> </p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=levelling-up&amp;topic=architecture">Architecture</a> </p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=levelling-up&amp;topic=performance">Performance</a></p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=levelling-up&amp;topic=big-tech-interviews">Big Tech Interviews</a></p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=levelling-up&amp;topic=ai-engineering">AI Engineering</a></p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=levelling-up&amp;topic=shipping-to-production">Shipping to Production</a></p></li></ul><h3><strong><a href="https://roadmap.jacobstechtavern.com/?level=deep-end">&#129497; The Deep End</a></strong></h3><p>For hardened, jaded engineers who want to grapple with the machinery under iOS and Swift that we use every day.</p><ul><li><p><a href="https://roadmap.jacobstechtavern.com/?level=deep-end&amp;topic=swift-internals">Swift Internals</a></p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=deep-end&amp;topic=ios-internals">iOS Internals</a> </p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=deep-end&amp;topic=advanced-concurrency">Advanced Concurrency</a> </p></li><li><p><a href="https://roadmap.jacobstechtavern.com/?level=deep-end&amp;topic=the-dark-arts">The Dark Arts&#8482;</a></p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dCXh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dCXh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png 424w, https://substackcdn.com/image/fetch/$s_!dCXh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png 848w, https://substackcdn.com/image/fetch/$s_!dCXh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png 1272w, https://substackcdn.com/image/fetch/$s_!dCXh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!dCXh!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png" width="1200" height="854.6703296703297" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1037,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:2561247,&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://blog.jacobstechtavern.com/i/209275290?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dCXh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png 424w, https://substackcdn.com/image/fetch/$s_!dCXh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png 848w, https://substackcdn.com/image/fetch/$s_!dCXh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png 1272w, https://substackcdn.com/image/fetch/$s_!dCXh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcabc6e3-e471-4df4-9472-e0fd5e253c28_2042x1454.png 1456w" sizes="auto, 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 class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://roadmap.jacobstechtavern.com&quot;,&quot;text&quot;:&quot;View the roadmap here&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://roadmap.jacobstechtavern.com"><span>View the roadmap here</span></a></p><p>I&#8217;ll send the roadmap to all my new readers, but please take a look yourself! I&#8217;d appreciate any feedback and hope you find it a useful tool.</p>]]></content:encoded></item><item><title><![CDATA[Designing The Perfect Modular Architecture]]></title><description><![CDATA[Applying everything I've learned across 10 years of building]]></description><link>https://blog.jacobstechtavern.com/p/modular-ios-architecture</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/modular-ios-architecture</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 29 Jul 2026 15:01:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Mwp_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Mwp_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Mwp_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!Mwp_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!Mwp_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!Mwp_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Mwp_!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:4378770,&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://blog.jacobstechtavern.com/i/207759946?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Mwp_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!Mwp_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!Mwp_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!Mwp_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff98b191-8547-4403-9d4a-5da40059a5b7_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>I&#8217;ve intentionally avoided treading the worn-out arguments about MVC, MVVM, MV, VIPER, and <a href="https://iosarchitecture.top/">your favourite homegrown solution</a>. I don&#8217;t consider it a gap in my blog content, because&#8230;</p><ol><li><p>They aren&#8217;t very interesting to write about.</p></li><li><p>I don&#8217;t consider this proper &#8220;architecture&#8221;. </p></li></ol><p>Your choice of screen-level file layout is just about <strong>consistency</strong>. Orienting yourself in an unfamiliar part of the codebase. Pick one, and stick to it. That&#8217;d be the whole blog post.</p><p>Proper Architecture&#8482; is about the <strong>building blocks of our system</strong>, and <strong>how they fit together</strong>. It&#8217;s as much art as science. Draw boundaries and boxes across your codebase, and gerrymander until your dependency graph sparks maximal joy.</p><p>Today, we&#8217;re going down my long-awaited hit-list:</p><ul><li><p>The what and why of modular iOS architecture.</p></li><li><p>Getting on the same page vis-a-vis dependency graphs.</p></li><li><p>The good, the bad, and the ugly architectures I&#8217;ve experienced in 10 years.</p></li><li><p>How I utilised this experience to design Granola&#8217;s modular architecture. </p></li></ul><p>By the end, I hope to transfer some of my architectural instincts to you.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h4>Contents</h4><ul><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/what-is-modular-ios-architecture">What is Modular iOS Architecture?</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/build-time">Build time</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/code-quality">Code quality</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/organisational-problems">Organisation problems</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/modular-solutions">Modular solutions</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/tooling-and-libraries">Tooling and Libraries</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/the-dependency-graph">The Dependency Graph</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/modular-architectures-ive-seen">Modular Architectures I&#8217;ve Seen</a></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/single-module">Single Module</a> </p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/core-module-architecture">Core Module Architecture</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/layered-modular-architecture">Layered Modular Architecture</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/naive-feature-modules">Na&#239;ve Feature Modules</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/the-problems-with-feature-modules">The problems with feature modules</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/service-layers">Service &amp; Orchestration Layers</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/feature-modules-with-api-contracts">Feature Modules with API Contracts</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/hyper-modular-architecture">Hyper-modular Architecture</a></p></li></ul></li><li><p><strong><a href="https://blog.jacobstechtavern.com/i/207759946/how-i-designed-granolas-architecture">How I Designed Granola&#8217;s Architecture</a></strong></p><ul><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/a-poc-with-big-dreams">A POC with big dreams</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/architectural-pushback">Architectural Pushback</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/this-is-my-design">This is My Design</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/the-big-refactor">The Big Refactor</a></p></li></ul></li><li><p><a href="https://blog.jacobstechtavern.com/i/207759946/last-orders">Last Orders</a></p></li></ul><div><hr></div><h2>What is Modular iOS Architecture?</h2><p>Many great iOS codebases begin as a big ball of stuff. One app, one target, one module, one project. Everything lives in one place, and making a new feature, service, or model is as simple as writing a file into a folder.</p><p>This works surprisingly well for longer than you&#8217;d think, but then you might start to feel the codebase <em>pushing back</em> on you. </p><div><hr></div><h4><strong>Build time</strong></h4><p>As a monolithic module grows, small changes might invalidate a larger portion of the dependency graph <em>(uh, dependency blob?)</em>. Incremental builds become&#8230; less incremental, code completion inevitably sloooows down, and Xcode struggles to cache reusable build outputs. </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_!M-7C!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png 424w, https://substackcdn.com/image/fetch/$s_!M-7C!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png 848w, https://substackcdn.com/image/fetch/$s_!M-7C!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png 1272w, https://substackcdn.com/image/fetch/$s_!M-7C!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!M-7C!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png" width="1200" height="271.15384615384613" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:329,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:964318,&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://blog.jacobstechtavern.com/i/207759946?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!M-7C!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png 424w, https://substackcdn.com/image/fetch/$s_!M-7C!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png 848w, https://substackcdn.com/image/fetch/$s_!M-7C!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png 1272w, https://substackcdn.com/image/fetch/$s_!M-7C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc1810a0a-a904-492f-aca8-2c2e8ee6cbdf_2459x555.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">Ask me about the Build Time Assistant in Xcode. Step 1 (highlighted): Compute dependency graph.</figcaption></figure></div><div><hr></div><h4><strong>Code quality</strong></h4><p><strong>With </strong>one module, you often cannot enforce meaningful boundaries at interfaces, because <strong>internal</strong> code (<a href="https://blog.jacobstechtavern.com/p/access-control-in-swift-like-a-boss">the default access control</a>) is visible everywhere within that module. You need discipline and lots of <strong>private</strong> methods to avoid spaghettification. </p><blockquote><p>You know what I mean if you&#8217;ve ever shamefully tasted that sweetest forbidden fruit: <em>turning that <strong>private</strong> function <strong>internal</strong> just so the unit test can speak directly to it</em>.</p></blockquote><p>It&#8217;s also mildly cruel to deprive junior developers of their first change to legitimately use the <strong>public</strong> keyword. Or better, <strong>public private(set)</strong>. You feel so badass the first time.</p><div><hr></div><h4><strong>Organisational problems </strong></h4><p>Often an architecture grows to reflect the shape of the organisation maintaining it. Companies with a platform team will often have a big Core module. &#8220;Feature teams&#8221; will have modules split up along pod lines.</p><p>When teams, pods, tribes, scrums, etc, are divided by feature ownership, but the codebase has no corresponding boundaries, it gets muddy fast. Who owns those services, that model, or this infra? Engineers may step on each other&#8217;s toes, re-implement solutions twice, or design wholly different APIs.</p><div><hr></div><h4>Modular solutions </h4><p>Dividing your app into modules gives your iOS app clearer boundaries between features and services, making the codebase easier to understand and explain. It allows you to enforce encapsulation, encourages reuse, and draws straightforward ownership boundaries across modules so tons of devs (or agents?) can work in parallel. </p><p>Modularity improves build times <strong>(usually!!*)</strong> by limiting how much code has to be recompiled after a change. If you edit one top-level feature module, Xcode can reuse its cached build outputs for non-dependent modules, compile independent modules in parallel, and cache more granular outputs. </p><blockquote><p><em>*There can be <a href="https://blog.jacobstechtavern.com/i/185205106/gluttony">too much of a good thing</a>. Make your dependency graph too complex and interconnected with many tiny, poorly-bounded modules, or create loooong, deeply-layered chains of dependencies, and you&#8217;ll just slow things down instead.</em></p></blockquote><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_!emwo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp 424w, https://substackcdn.com/image/fetch/$s_!emwo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp 848w, https://substackcdn.com/image/fetch/$s_!emwo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp 1272w, https://substackcdn.com/image/fetch/$s_!emwo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!emwo!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp" width="1200" height="122.8021978021978" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:149,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:7976,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/207759946?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!emwo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp 424w, https://substackcdn.com/image/fetch/$s_!emwo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp 848w, https://substackcdn.com/image/fetch/$s_!emwo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp 1272w, https://substackcdn.com/image/fetch/$s_!emwo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffbe7ad62-807d-4a4b-9c1d-477c97aa7be4_1456x149.webp 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">This is actually a quite nice dependency graph, redacted to protect the innocent</figcaption></figure></div><p>We waited an appropriately long time to refactor Granola: I&#8217;ll go into more detail soon, but our ultimate modular redesign was motivated by a combination of 1) team growth and 2) some very specific pushback we were experiencing from the codebase.</p><div><hr></div><h4>Tooling and Libraries</h4><p>When you want to modularise your app, the default is SPM. It used to be bad. It&#8217;s good. I like Tuist. Rolling raw Xcode projects are probably fine if you get Claude to manage the boilerplate. CocoaPods and Carthage are mostly-deprecated, you maniac. Don&#8217;t touch Buck or Bazel unless you are willing to open that particular Pandora&#8217;s box, and maintain it forever. </p><p>When you create a module, you need to choose whether you&#8217;ll use a static, dynamic, or mergeable library. These are incredibly important for modularisation, but very out of scope for this article. To make a long story short, pick between them to balance build speed, bundle size, and launch time. To make a long story long, you can read this classic of mine:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;4fd6ef6c-268b-4c13-8bb0-14351ce75b29&quot;,&quot;caption&quot;:&quot;If you want to embarrass a senior iOS engineer, ask them to explain the difference between Dynamic Frameworks and Static Libraries.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;md&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Static, Dynamic, Mergeable, oh, my!&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Master iOS. Boost your salary. Join 100,000 senior Swift devs learning advanced concurrency, SwiftUI, and iOS performance for 10 minutes a week. Sign up free today!&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2024-11-18T17:24:06.203Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ae622791-8353-49da-89a6-611e01a0e3f5_1680x1200.png&quot;,&quot;cover_image_alt&quot;:null,&quot;cover_image_crop&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/static-dynamic-mergeable-oh-my&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:149938916,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:74,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><h2>The Dependency Graph</h2><p>One important concept in modularising an app: the dependency graph. Consider the modules as boxes or nodes along a graph. Yes, the data structure (<em>you thought LeetCode was a waste of time?!).</em> One node can&#8217;t compile unless all its dependencies, or <strong>imports</strong>, are compiled. </p><p>Your app builds the <strong>leaf nodes</strong> at the bottom of the graph, then modules depending on them, then modules depending on <em>them</em>, recursively, all the way up to your top level app module that ties everything together. While it can parallelise a lot of this, you can&#8217;t build a node without its dependencies. You get it.</p><p>This is why spaghetti architectures can slow down your builds: one tiny change can invalidate the dependency graph of dozens or hundreds of modules importing it across your graph.  </p><p>Some of the architectures we&#8217;re looking at today are optimised with the Xcode Build system in mind. This is actually the core insight behind Tuist&#8217;s TMA and &#8220;Feature Interface&#8221; API contracts:</p><blockquote><p><em><strong>If your module&#8217;s public interface stays the same, modules depending on you will often not need recompilation, even if your module changes internally.</strong></em></p></blockquote><div><hr></div><h2><strong>Modular Architectures I&#8217;ve Seen</strong></h2><p>I <a href="https://blog.jacobstechtavern.com/p/modular-architecture-for-apps">first wrote about modular architecture</a> 2.5 years ago, somewhat early in my blogging career. For a while, I was embarrassed to share it, because I evangelised an architecture that had some serious scaling issues. Amateur hour. I&#8217;m more nuanced these days. Simple can be better.</p><p>Let&#8217;s take a look at some of the architectures I&#8217;ve experienced over 10 years of engineering, and dozens of projects. </p><div><hr></div><h4><strong>Single Module</strong> </h4><p>Not much to say about this tbh! </p><p>Prior to 2019, every app I built was a single module; as is almost every indie project I touch today. If build times aren&#8217;t a problem, or you&#8217;re working alone, why take the time?*</p><blockquote><p><em>*That said, I often ask my agent to split up my projects if I get where I want to outside of my token limits. It makes me happy, even if I&#8217;m the only one who cares.</em></p></blockquote><div><hr></div><h4>Core Module Architecture</h4><p>If you ever decide to modularise your app, but you don&#8217;t have much time, this is usually how you&#8217;ll start. It&#8217;s a 2-parter:</p><ul><li><p><strong>App</strong><span>, containing features and your top level target.</span></p></li><li><p><strong>Core</strong>,<span> with everything else: models, networking, persistence, auth, utils, analytics, and shared UI.</span></p></li></ul><p>This approach is nice for multi-target apps. In <a href="https://blog.jacobstechtavern.com/p/the-side-hustle-from-hell">The Side Hustle From Hell</a>, we foolishly tried to create a 2-sided marketplace, with apps for car mechanics and punters whose engine just conked out. On iOS and Android. 4 apps. Zero users. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!U14n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!U14n!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png 424w, https://substackcdn.com/image/fetch/$s_!U14n!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png 848w, https://substackcdn.com/image/fetch/$s_!U14n!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png 1272w, https://substackcdn.com/image/fetch/$s_!U14n!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!U14n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png" width="800" height="758" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:758,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&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="" title="" srcset="https://substackcdn.com/image/fetch/$s_!U14n!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png 424w, https://substackcdn.com/image/fetch/$s_!U14n!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png 848w, https://substackcdn.com/image/fetch/$s_!U14n!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png 1272w, https://substackcdn.com/image/fetch/$s_!U14n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bf10a87-72b0-443a-9957-a3886d0eb677_800x758.png 1456w" sizes="auto, 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">Classical Cocoapods-powered module architecture for Patcher; Core shared between 2 app targets</figcaption></figure></div><p>The Core module is also a natural fit for Kotlin Multiplatform or organisations with a dedicated platform team.</p><div><hr></div><h4>Layered Modular Architecture</h4><p>The first time I saw this architecture was on a top-secret electric car project which, <a href="https://www.bbc.co.uk/news/business-50004184">I found out while in the pub</a>, got abruptly sh*tcanned. An extremely cracked engineer, who rapidly left our company for more money, built a 3-tier layered app architecture with FeatureKit, ModelKit, and NetworkKit. </p><p>It was the first modular app I&#8217;d touched. And the most exciting thing I&#8217;d seen.</p><p>This architecture groups common functionality together into layers that serve the data requirements of the layer above, enforcing a highly unidirectional data flow through each layer.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;swift&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-swift">App   
&#8595; &#8593;
Repository
&#8595; &#8593;
Networking</code></pre></div><p>This architecture made an impression. I adopted the same structure for <a href="https://github.com/jacobsapps/bev17">Bev</a>, my original magnum opus (back when that word <em>meant</em> something). The top-level <strong>Bev</strong> module imports a Repository, which in turn <a href="https://blog.jacobstechtavern.com/p/unit-test-the-observation-framework">synthesised the most up-to-date data</a> from both our local Database and Networking modules.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NUT0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NUT0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png 424w, https://substackcdn.com/image/fetch/$s_!NUT0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png 848w, https://substackcdn.com/image/fetch/$s_!NUT0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png 1272w, https://substackcdn.com/image/fetch/$s_!NUT0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!NUT0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png" width="800" height="621" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:621,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&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="" title="" srcset="https://substackcdn.com/image/fetch/$s_!NUT0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png 424w, https://substackcdn.com/image/fetch/$s_!NUT0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png 848w, https://substackcdn.com/image/fetch/$s_!NUT0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png 1272w, https://substackcdn.com/image/fetch/$s_!NUT0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bea73f9-4188-41e5-80d4-34cb7394d3b7_800x621.png 1456w" sizes="auto, 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">Bev&#8217;s layered modular architecture</figcaption></figure></div><p>This is nice for very simple projects, but begins to get tricksy once you add authentication, analytics, models, business logic, arbitrary services, and <em>utilities</em>. Where do you place those? </p><p>Unless your project remains very simple, you will feel the codebase nudging you towards a multi-modular setup inside each layer.</p><div><hr></div><h4>Na&#239;ve Feature Modules</h4><p>Feature modules are the divergent evolution of your initial Core module. Instead of focusing on Layers and business logic, you might want to split your top-level UI layer into many; usually one module per feature.</p><p>My first startup, Carbn, eventually got here after playing around with a Core module to start with (rolling pure .xcprojects!), and my subsequent role at Gener8 had the same shape, set up using modern SPM.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CiYC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CiYC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png 424w, https://substackcdn.com/image/fetch/$s_!CiYC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png 848w, https://substackcdn.com/image/fetch/$s_!CiYC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png 1272w, https://substackcdn.com/image/fetch/$s_!CiYC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!CiYC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png" width="800" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/faedb29b-de67-4d28-8b18-449c55b04957_800x558.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&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="" title="" srcset="https://substackcdn.com/image/fetch/$s_!CiYC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png 424w, https://substackcdn.com/image/fetch/$s_!CiYC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png 848w, https://substackcdn.com/image/fetch/$s_!CiYC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png 1272w, https://substackcdn.com/image/fetch/$s_!CiYC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffaedb29b-de67-4d28-8b18-449c55b04957_800x558.png 1456w" sizes="auto, 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">Notice I still called the middle layer &#8220;ModelKit&#8221;.</figcaption></figure></div><p>If you work on a legacy app that wants to modularise, and you aren&#8217;t thinking that hard about it, you&#8217;ll probably land on feature modules. It offers nice ownership boundaries, excellent build time benefits, and two <em>screaming</em> weaknesses that I didn&#8217;t explain properly in <a href="https://blog.jacobstechtavern.com/p/modular-architecture-for-apps">my first architecture article</a>. </p><div><hr></div><h4>The problems with feature modules </h4><ol><li><p><em><strong>&#8220;Where do we put shared business logic?</strong></em><strong>&#8221;</strong></p></li></ol><p>It&#8217;s nice to give feature modules ownership over their own models and services, but suddenly you&#8217;re contending with awkward refactors whenever <em>another</em> feature needs it.</p><p>Eventually, you either throw models, services, and logic back into a shared <strong>Core</strong> module, or endure an uncomfortable hybrid with <em>some</em> in the Core and <em>some</em> domain-specific stuff living in the features.</p><p>This is the first bit of resistance from your codebase you should feel, and <em>keenly</em>. Business logic and services mostly want to live in their own modules. Architecture wants to be consistent, and having to think hard about where to put your new model or service is a sign of a bad architecture. </p><ol start="2"><li><p><em><strong>&#8220;How do we reuse these screens?</strong></em><strong>&#8221;</strong></p></li></ol><p>At Gener8, our clean SPM-defined feature modules worked great: we had a coordinator per module that could transition you between any screen in the module, or kick off a new flow to enter a new module.</p><p>Until, one day, I was asked to present UI from our Rewards module on the Home screen. </p><p>F*ck!</p><p>My beautifully-designed feature module architecture fell apart here. I had to create a low-level shared shim protocol, inject it at the top level, and wrap it in AnyView to allow Home to present the typed Rewards UI element.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tOql!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tOql!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg 424w, https://substackcdn.com/image/fetch/$s_!tOql!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg 848w, https://substackcdn.com/image/fetch/$s_!tOql!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!tOql!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!tOql!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg" width="1206" height="928" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:928,&quot;width&quot;:1206,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:165773,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/207759946?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tOql!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg 424w, https://substackcdn.com/image/fetch/$s_!tOql!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg 848w, https://substackcdn.com/image/fetch/$s_!tOql!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!tOql!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F327dd542-a5dd-4809-beec-c2c7cab12739_1206x928.jpeg 1456w" sizes="auto, 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">I remember like it was yesterday. It was the &#8220;Featured Reward&#8221; UI.</figcaption></figure></div><p>This is another burning red flag &#128681;. Creating a tiny shared protocol to let one screen talk to another is the quintessential architectural pushback I&#8217;ve been blathering on about. This&#8230; <em>worked</em>&#8230; but the codebase was not happy with the way I was torturing it.</p><p>This solution was, fundamentally, a factory protocol, but I couldn&#8217;t have articulated it at the time.</p><p>Both of these problems were solved in codebases I worked on later in my career.</p>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/modular-ios-architecture">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[My Top 3 Design “Kisses” I Like to Add to Every App]]></title><description><![CDATA[Several somewhat sexy unrequited design flourishes]]></description><link>https://blog.jacobstechtavern.com/p/design-kisses</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/design-kisses</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 22 Jul 2026 15:00:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!SZ-p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SZ-p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SZ-p!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif 424w, https://substackcdn.com/image/fetch/$s_!SZ-p!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif 848w, https://substackcdn.com/image/fetch/$s_!SZ-p!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif 1272w, https://substackcdn.com/image/fetch/$s_!SZ-p!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SZ-p!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif" width="1456" height="1040" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7758656,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/203047335?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SZ-p!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif 424w, https://substackcdn.com/image/fetch/$s_!SZ-p!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif 848w, https://substackcdn.com/image/fetch/$s_!SZ-p!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif 1272w, https://substackcdn.com/image/fetch/$s_!SZ-p!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe1678fd-16f5-4d25-b628-cdd7299b51f9_1680x1200.gif 1456w" sizes="100vw" fetchpriority="high"></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>At a corporate motivational event once, I found out it&#8217;s good to kiss your customers.</p><p>Not in a 2017, problematic, HR-esque way.</p><p>A &#8220;kiss&#8221; is just &#8220;a nice thing you didn&#8217;t have to do&#8221;. A chocolate on the pillow. The catch-up call where you don&#8217;t sell anything. The plus-one to an event. That tiny bit of satisfying design polish that makes your users think, right, these guys give a sh*t.</p><p>I&#8217;ve been adding these to every app I&#8217;ve touched recently:</p><ul><li><p>Progressive blurs for otherworldly beauty</p></li><li><p>Metal shaders for skeuomorphic texture</p></li><li><p>Responsive haptics for an alive feel</p></li></ul><p>You don&#8217;t have to use these kisses. But if you do, your users may just become infatuated with your app.</p><div><hr></div><h1><strong>Progressive Blur</strong></h1><p>First there was a blocky blue nav bar. Then a clean, minimal white block. Then Material. At some point <em>(I&#8217;ve blocked it out from my memory)</em> Apple went all Liquid Glass on our asses. It was the future, <a href="https://notes.granola.ai/d/974d3980-f848-41ad-ab13-db4ccf1a66b8?suppressDownloadAlert=true">until it wasn't</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_!Q-iN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png 424w, https://substackcdn.com/image/fetch/$s_!Q-iN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png 848w, https://substackcdn.com/image/fetch/$s_!Q-iN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png 1272w, https://substackcdn.com/image/fetch/$s_!Q-iN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Q-iN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png" width="1456" height="233" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:233,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1537093,&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://blog.jacobstechtavern.com/i/203047335?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.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_!Q-iN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png 424w, https://substackcdn.com/image/fetch/$s_!Q-iN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png 848w, https://substackcdn.com/image/fetch/$s_!Q-iN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png 1272w, https://substackcdn.com/image/fetch/$s_!Q-iN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9a51f21-2f59-475a-9082-adfdc4774148_2738x438.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div><figcaption class="image-caption">I couldn&#8217;t be bothered to use Canva here, but fortunately GPT-5.5 Pro imagegen did a surprisingly serviceable job </figcaption></figure></div><p>But this isn&#8217;t sexy. Or frosty.</p><p>Progressive blurs just look really damn nice. You know?</p><p>You scroll up, the blur comes out of nowhere, and the blur radius bubbles smoothly up towards the screen edge, smearing your app&#8217;s colours as it ascends to the void, towards heaven, in an otherworldly, almost Thanosian evaporation.</p><p>Enough linguistic fapping. You just need to see it.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;20b05921-1230-44d8-b484-41955fd4b183&quot;,&quot;duration&quot;:null}"></div><div class="callout-block" data-callout="true"><p>I can&#8217;t wait to share the magic of progressive blurs, SwiftUI Metal shaders, and responsive haptics with you. And if you&#8217;re a free subscriber, I bet you, uh, can&#8217;t wait to give me money. But you&#8217;ll have to wait a little longer with this 2-week free trial:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=203047335&quot;,&quot;text&quot;:&quot;Get 14 day free trial&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=203047335"><span>Get 14 day free trial</span></a></p><p>Paid members get several kisses:</p><p><span>&#9875;&#65039; Access my </span><a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 50 paywalled articles</a><span> (including this one)</span><br><span>&#128640; Read </span><a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a><span> a month before anyone else</span><br><span>&#129525; Master concurrency with my </span><a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a><span> and </span><a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br><span>&#129489;&#8205;&#128640; Get a free copy of my new eBook, &#8220;</span><a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a><span>&#8221;</span><br><span>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</span></p></div>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/design-kisses">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[The history (and future) of technology form factors]]></title><description><![CDATA[And the hunt for a billion dollars]]></description><link>https://blog.jacobstechtavern.com/p/technology-form-factors</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/technology-form-factors</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 15 Jul 2026 15:01:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!htuU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!htuU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!htuU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!htuU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!htuU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!htuU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!htuU!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:3656545,&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://blog.jacobstechtavern.com/i/197332009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!htuU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!htuU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!htuU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!htuU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f29bfc0-9273-49e3-a40d-ae0c4f8f1cb1_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>This is not a super-technical deep dive. But I&#8217;ve been thinking it for a long time. </p><p>It&#8217;s a piece about UX. About form factors; past and future. Think of it as a palate cleanser between deep-dives on concurrency, Metal, and job interviews. </p><p>Chill (&#9924;&#65039;).</p><p>Whoever cracks this is going to make a billion dollars. Legit. That&#8217;s <strong>5.5 million years of Premium Membership</strong> to Jacob&#8217;s Tech Tavern. Perhaps a few more if you subscribe annually.</p><p>But, before we get there, a history lesson &#129489;&#8205;&#127979;</p><div><hr></div><h5>Sponsored Link</h5><blockquote><h4><a href="https://www.revenuecat.com/blog/company/announcing-shipaton-2026?utm_medium=sponsored&amp;utm_source=JacobsTechTavern&amp;utm_campaign=newsletter&amp;utm_content=shipaton">Shipaton 2026: build and launch your next iOS app</a></h4><p>Indie iOS devs: this is your excuse to finally ship. Shipaton 2026 is RevenueCat&#8217;s hackathon for app builders, with over $1M worth of prizes. Build a new iOS app, add RevenueCat, and launch between August 1st and September 30th.</p><p>Check it out and register <a href="https://www.revenuecat.com/blog/company/announcing-shipaton-2026?utm_medium=sponsored&amp;utm_source=JacobsTechTavern&amp;utm_campaign=newsletter&amp;utm_content=shipaton">here</a>.</p></blockquote><div><hr></div><h3>Inception</h3><p>In 2007, the iPhone was announced. Like ChatGPT 15 years later, or Netscape Navigator 13 years prior, it ushered in the next great technological era: Mobile. In one swoop*, consumers had access to:</p><ul><li><p>Multi-touch capacitive screen</p></li><li><p>A desktop-class web browser</p></li><li><p>Cellular and Wi-Fi networking</p></li><li><p>GPS and location </p></li><li><p>Motion sensors</p></li><li><p>A decent photo and video camera </p></li><li><p>An App Store that made your device more valuable every day</p></li></ul><p><strong>And you got all this in a portable handheld device!</strong></p><blockquote><p><em>*two swoops if you pedantically point out that the App Store/GPS/video didn&#8217;t arrive &#8216;till 2008. I see you. </em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RTqq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RTqq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!RTqq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg" width="1280" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:37049,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/197332009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!RTqq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RTqq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc906facb-2b8f-41b2-9527-873db8b630f0_1280x720.jpeg 1456w" sizes="auto, 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">&#8220;3 things. A widescreen touch-screen iPod, a revolutionary mobile phone, and a breakthrough internet communications device. Are you getting it?&#8221;</figcaption></figure></div><p>Early apps got nearly-free distribution on relatively crappy hardware. To-do lists, fart apps, and <a href="https://en.wikipedia.org/wiki/I_Am_Rich">static PNGs</a> were making tens of thousands of dollars*.</p><blockquote><p><em>*It&#8217;s true. I once worked with a greybeard who paid his mortgage by shipping a calendar app in 2010 and getting untold thousands of paid downloads.</em></p></blockquote><p>Heavy hitters like Facebook scrambled to stake a claim in this new land-grab. So Zuck did what he knew best, shipping an <a href="https://techcrunch.com/2012/09/11/mark-zuckerberg-our-biggest-mistake-with-mobile-was-betting-too-much-on-html5/">HTML5 app</a> that ported the existing news feed from the web.</p><p><em>Nobody</em> knew how to make the most of the new platform. Not Meta. Not Apple. Not even Instagram. </p><p>In 2010, this mobile-native new challenger wrote <a href="https://blog.jacobstechtavern.com/p/core-image-the-basics">OpenGL shaders</a> to filter photos and squeeze out value from the meagre early camera hardware, transforming your iDevice into a surface for <em>creation</em> as well as <em>consumption</em>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BDlM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BDlM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png 424w, https://substackcdn.com/image/fetch/$s_!BDlM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png 848w, https://substackcdn.com/image/fetch/$s_!BDlM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png 1272w, https://substackcdn.com/image/fetch/$s_!BDlM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!BDlM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png" width="547" height="482" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:482,&quot;width&quot;:547,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:402644,&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://blog.jacobstechtavern.com/i/197332009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.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_!BDlM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png 424w, https://substackcdn.com/image/fetch/$s_!BDlM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png 848w, https://substackcdn.com/image/fetch/$s_!BDlM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png 1272w, https://substackcdn.com/image/fetch/$s_!BDlM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F546f4916-a01e-4c75-8347-6c1524f935d2_547x482.png 1456w" sizes="auto, 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">Early Instagram (2012), from <a href="https://mobilesyrup.com/2012/09/26/instagram-updated-with-iphone-5-support-bug-fixes/">MobileSyrup</a></figcaption></figure></div><p>Many of the big hitters you know and love to hate began in this early scramble: Uber. Snapchat. Instagram. WhatsApp. Tinder. Waze. Erm, Yik Yak. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h3>Business Model Innovation</h3><p>In 2012, Facebook finally got its sh*t together and figured out how to monetise mobile, placing ads natively alongside items in the news feed.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6omF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6omF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg 424w, https://substackcdn.com/image/fetch/$s_!6omF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg 848w, https://substackcdn.com/image/fetch/$s_!6omF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!6omF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!6omF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg" width="1260" height="840" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:840,&quot;width&quot;:1260,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:156594,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/197332009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!6omF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg 424w, https://substackcdn.com/image/fetch/$s_!6omF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg 848w, https://substackcdn.com/image/fetch/$s_!6omF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!6omF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F880ef1a0-656c-476e-82e7-11c055078386_1260x840.jpeg 1456w" sizes="auto, 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">Facebook news feed ads, from <a href="https://newsfeed.org/mobile-facebook-ads-1260x840">newsfeed.org</a></figcaption></figure></div><p>In hindsight, this seems <em>so obvious</em>, but consider the perspective of <em>the most beloved product that ever existed on the web</em>. I&#8217;m not kidding. <a href="https://podscripts.co/podcasts/acquired/meta">For </a><strong><a href="https://podscripts.co/podcasts/acquired/meta">years</a></strong><a href="https://podscripts.co/podcasts/acquired/meta">, 70% of people who had ever registered for Facebook were daily active users</a>. </p><p>Your users are used to unobtrusive banner ads. How could you risk polluting the <em>news feed</em>, risking a defection to Instagram and WhatsApp*, for an unproven revenue opportunity?</p><blockquote><p><em>*Remember those glory days when they weren&#8217;t all the same company?</em></p></blockquote><p>Selling ads as content-ish objects, integrated alongside infinite updates from your friends, turned out to be drastically more profitable than the web could ever dream.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lkOy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lkOy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif 424w, https://substackcdn.com/image/fetch/$s_!lkOy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif 848w, https://substackcdn.com/image/fetch/$s_!lkOy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif 1272w, https://substackcdn.com/image/fetch/$s_!lkOy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!lkOy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif" width="1260" height="712" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:712,&quot;width&quot;:1260,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29356,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/avif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/197332009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lkOy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif 424w, https://substackcdn.com/image/fetch/$s_!lkOy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif 848w, https://substackcdn.com/image/fetch/$s_!lkOy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif 1272w, https://substackcdn.com/image/fetch/$s_!lkOy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d532474-b061-4a42-af68-df4ee1a5ba2f_1260x712.avif 1456w" sizes="auto, 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">That huge spike after the long IPO slump comes from their Q2 2013 earnings, which first showed mobile making up ~50% of ad revenue.</figcaption></figure></div><div><hr></div><h3>Form Factor Innovation</h3><p>Social was the hottest industry in early-2010s-startupland. Business model innovation is nice, but the cool kids (who adopt your product) could not care less if you were monetising them. They probably also didn&#8217;t care that much about their data at the time. I dunno. I wasn&#8217;t one of them.</p><p>The big story is <em>form factor</em>. Well. The form factor is Story. Stories, that is. </p><p><em>Ahem.</em></p><p>Late 2013. Snapchat unleashed Stories on the world.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nGzB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nGzB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg 424w, https://substackcdn.com/image/fetch/$s_!nGzB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg 848w, https://substackcdn.com/image/fetch/$s_!nGzB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!nGzB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!nGzB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg" width="1456" height="832" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:832,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:235817,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/197332009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nGzB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg 424w, https://substackcdn.com/image/fetch/$s_!nGzB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg 848w, https://substackcdn.com/image/fetch/$s_!nGzB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!nGzB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdf97193-2192-4174-92f9-c15e040402b8_2100x1200.jpeg 1456w" sizes="auto, 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">Stories feature announcement and early UI, from <a href="https://techcrunch.com/2013/10/03/snapchat-gets-its-own-timeline-with-snapchat-stories-24-hour-photo-video-tales/">TechCrunch</a></figcaption></figure></div><p>At last, there was a form factor that felt perfect for mobile, on both the creation and consumption sides:</p><ul><li><p>It used the hardware camera as the starting point; vertical by default </p></li><li><p>It took Snapchat&#8217;s ephemeral messaging and turned it into broadcasting </p></li><li><p>This ephemerality encouraged mundane, casual, &#8220;low pressure&#8221; posting</p></li><li><p>This, in turn, encouraged repeat daily posting and consumption </p></li><li><p>Fullscreen consumption captured your whole attention and encouraged &#8220;tapping through&#8221; all the updates from your friends.</p></li></ul><p>When you put everything together like that, it was obviously the ideal form factor for an early mobile device. </p><p>But also, it still wasn&#8217;t that obvious without hindsight.</p><p>In 2016, the <em>problem of pressure to post the perfect photo</em> began to really hurt engagement on Insta. Zuckko ripped the stories form factor straight off <a href="https://en.wikipedia.org/wiki/Evan_Spiegel">Speegz</a>. At which point, he decided to yeet it everywhere.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ftIm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ftIm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png 424w, https://substackcdn.com/image/fetch/$s_!ftIm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png 848w, https://substackcdn.com/image/fetch/$s_!ftIm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png 1272w, https://substackcdn.com/image/fetch/$s_!ftIm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ftIm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png" width="1456" height="740" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:740,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2249139,&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://blog.jacobstechtavern.com/i/197332009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.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_!ftIm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png 424w, https://substackcdn.com/image/fetch/$s_!ftIm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png 848w, https://substackcdn.com/image/fetch/$s_!ftIm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png 1272w, https://substackcdn.com/image/fetch/$s_!ftIm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e49177e-b09e-4c58-b14a-aaa1d9aeb031_2404x1222.png 1456w" sizes="auto, 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">Remember back in 2017 when everyone was memeing about stories being everywhere? From <a href="https://www.reddit.com/r/funny/comments/63diku/calculators_in_2017_now_have_stories/">r/funny</a></figcaption></figure></div><div><hr></div><h3>The extremely obvious final form</h3><p>Today, people no longer casually consume content shared by their acquaintances. Social media has bifurcated into private messaging with close friends; and watching videos made by strangers. Whatever market opportunity there was for catching up with acquaintances has dried up. </p><p>TikTok, Instagram Reels, YouTube Shorts, and Snapchat Spotlight <em>(I had to look up the name)</em> have converged on pretty much the exact same form factor. But, again, <em>it all seemed so obvious!</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ady1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ady1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif 424w, https://substackcdn.com/image/fetch/$s_!Ady1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif 848w, https://substackcdn.com/image/fetch/$s_!Ady1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif 1272w, https://substackcdn.com/image/fetch/$s_!Ady1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ady1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif" width="828" height="466" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:466,&quot;width&quot;:828,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:45569,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/avif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/197332009?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ady1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif 424w, https://substackcdn.com/image/fetch/$s_!Ady1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif 848w, https://substackcdn.com/image/fetch/$s_!Ady1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif 1272w, https://substackcdn.com/image/fetch/$s_!Ady1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53a8e253-2aff-4b60-9acb-cf3b01e1084e_828x466.avif 1456w" sizes="auto, 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">Snapchat spotlight, TikTok, and Instagram Reels. Because I am 31 years old, I don&#8217;t know which is which. From <a href="https://www.bustle.com/life/tiktok-reels-snapchat-spotlight-compared">bustle.com</a></figcaption></figure></div><p>TikTok burst onto the scene in 2019, and forced everyone to adopt its obviously superior form factor (or perish).</p><p>It brought the logical conclusion of the fullscreen stories format. Instead of an image, it&#8217;s a video. Instead of a crappy 4&#8221; screen, it&#8217;s on a stonking 6.5&#8221; chad of a glass rectangle. Instead of disappearing after six seconds, it&#8217;s broadcast to the world in a ruthless Darwinian survival game of attention.</p><p>On-demand full-screen video was <a href="https://link.springer.com/chapter/10.1007/978-1-84882-701-1_21">a predictable ultimate form factor way back in 2010</a>. But failure to implement this wasn&#8217;t due to a lack of creativity on those would-be Gen X entrepreneurs.</p>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/technology-form-factors">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Apple Photos and Face Recognition in 512-D Hyperspace]]></title><description><![CDATA[Everyday ML to delight your users]]></description><link>https://blog.jacobstechtavern.com/p/apple-photos-and-face-recognition</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/apple-photos-and-face-recognition</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Tue, 07 Jul 2026 15:03:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!x9Ac!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!x9Ac!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!x9Ac!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!x9Ac!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!x9Ac!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!x9Ac!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!x9Ac!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:3838009,&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://blog.jacobstechtavern.com/i/191349414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!x9Ac!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!x9Ac!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!x9Ac!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!x9Ac!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7dcb04a4-c352-4b6e-887e-3736478d48d2_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>AI features are frequently whipped out and waved about in your face. You know what I&#8217;m talking about: empathetic but useless customer service droids. Auto-post-writing on social media. Email summaries (and filtering!) that nobody asked for.</p><p>Often, we&#8217;re not really the target audience; it&#8217;s so they can tell shareholders about their AI strategy <em>(annoying but I guess better than what Meta is doing with the 20% layoffs&#8230;)</em></p><p>It&#8217;s not controversial to say the best kind of AI features are invisible, quietly improving products without you having to think about it. Recommender algorithms. Curation. Semantic search.</p><p>Apple Photos is a great case study. Say what you want about the dodgy iOS 26 redesign, but it&#8217;s fundamentally a solid product: it safeguards decades of your life in the cloud. It curates daily slideshows of memories. And it&#8217;s pretty darn good at working out who is who across your photos.</p><p><em>People &amp; Pets</em> lets you tap a face to see all the photos of that person.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dv0P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dv0P!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png 424w, https://substackcdn.com/image/fetch/$s_!Dv0P!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png 848w, https://substackcdn.com/image/fetch/$s_!Dv0P!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png 1272w, https://substackcdn.com/image/fetch/$s_!Dv0P!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dv0P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png" width="1456" height="1034" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1034,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1446184,&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://blog.jacobstechtavern.com/i/191349414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.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_!Dv0P!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png 424w, https://substackcdn.com/image/fetch/$s_!Dv0P!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png 848w, https://substackcdn.com/image/fetch/$s_!Dv0P!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.png 1272w, https://substackcdn.com/image/fetch/$s_!Dv0P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa766f87-276c-455e-abdb-50d1c0b614f4_1518x1078.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><figcaption class="image-caption">Apple Photos &#8220;People &amp; Pets&#8221; filters photos by face.</figcaption></figure></div><p>Facial recognition isn&#8217;t possible without some <em>solid</em> ML work: computing facial fingerprints across thousands of images and clustering like images into a library of family and friends, all in the background.</p><p>There&#8217;s no better way to understand a system than by jerry-rigging your own janky version, so that&#8217;s exactly what I did.</p><div><hr></div><h2>Contents</h2><ul><li><p><a href="https://blog.jacobstechtavern.com/i/191349414/the-sample-project">The Sample Project</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/191349414/step-1-finding-faces-with-vision">Step #1: Finding Faces with Vision</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/191349414/step-2-computing-embeddings">Step #2: Computing Embeddings</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/191349414/step-3-the-clustering-pipeline">Step #3: The Clustering Pipeline</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/191349414/step-4-optimising-the-pipeline">Step #4: Optimising the Pipeline</a></p></li><li><p><a href="https://blog.jacobstechtavern.com/i/191349414/last-orders">Last Orders</a></p></li></ul><div><hr></div><h2>The Sample Project</h2><p>I couldn&#8217;t be arsed to decompile anything today, so can&#8217;t speak for Apple&#8217;s specific implementation, but the concepts of facial recognition software are a surprisingly straightforward 3-step pipeline:  </p><ol><li><p>Detect faces in each photo via Vision.</p></li><li><p>Turn each detected face into an embedding via ML models.</p></li><li><p>Group similar embeddings into people via clustering algorithms.</p></li></ol><p>The best part? This all works locally with on-device models, and works pretty damn well. </p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;a5048a72-5dcb-48f0-839d-a228fe2c6e31&quot;,&quot;duration&quot;:null}"></div><p>I&#8217;ll also share the sample project so you can go into much more detail. We will learn a whole lot about embeddings along the way. Quick spoiler for where we&#8217;re landing:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EAcm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EAcm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png 424w, https://substackcdn.com/image/fetch/$s_!EAcm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png 848w, https://substackcdn.com/image/fetch/$s_!EAcm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png 1272w, https://substackcdn.com/image/fetch/$s_!EAcm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!EAcm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png" width="834" height="894" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:894,&quot;width&quot;:834,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:395336,&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://blog.jacobstechtavern.com/i/191349414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.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_!EAcm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png 424w, https://substackcdn.com/image/fetch/$s_!EAcm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png 848w, https://substackcdn.com/image/fetch/$s_!EAcm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png 1272w, https://substackcdn.com/image/fetch/$s_!EAcm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d101f4e-8709-4b0d-973e-adaf36de7f77_834x894.png 1456w" sizes="auto, 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">Projecting my 512-dimensional face embeddings into 3D geometry</figcaption></figure></div><p>Get the whole sample project on my GitHub repo now:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://github.com/jacobsapps/Embeddingz&quot;,&quot;text&quot;:&quot;Get the sample project&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://github.com/jacobsapps/Embeddingz"><span>Get the sample project</span></a></p><div><hr></div><h2><strong>Step #1: Finding Faces with Vision</strong></h2><p>The first step to building a library of faces is understanding which pixels of a given image are interesting to you.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1QtF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1QtF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png 424w, https://substackcdn.com/image/fetch/$s_!1QtF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png 848w, https://substackcdn.com/image/fetch/$s_!1QtF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png 1272w, https://substackcdn.com/image/fetch/$s_!1QtF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!1QtF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png" width="1308" height="1056" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1056,&quot;width&quot;:1308,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2075915,&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://blog.jacobstechtavern.com/i/191349414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.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_!1QtF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png 424w, https://substackcdn.com/image/fetch/$s_!1QtF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png 848w, https://substackcdn.com/image/fetch/$s_!1QtF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png 1272w, https://substackcdn.com/image/fetch/$s_!1QtF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb43d5aed-c709-4b8e-9fa5-132584f03f60_1308x1056.png 1456w" sizes="auto, 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">Me and the boys at the pub, alongside some very subtle facial detection (redacting James&#8217; face because he works for MI6)</figcaption></figure></div><p>Now while I think all pixels are beautiful, only a few bits of the image actually help us with facial recognition. That is, the faces. A photo has a vast quantity of information, but most of it is noise if you just want to match faces. </p><p>We need to find the coordinates of the image which actually contain faces.</p><p>Apple&#8217;s Vision framework has a built-in API for precisely this. Give <strong>VNDetectFaceRectanglesRequest</strong> an image, and it returns a list of bounding boxes around each face.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2-wf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2-wf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png 424w, https://substackcdn.com/image/fetch/$s_!2-wf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png 848w, https://substackcdn.com/image/fetch/$s_!2-wf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png 1272w, https://substackcdn.com/image/fetch/$s_!2-wf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!2-wf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png" width="1456" height="807" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:807,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:395760,&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://blog.jacobstechtavern.com/i/191349414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.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_!2-wf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png 424w, https://substackcdn.com/image/fetch/$s_!2-wf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png 848w, https://substackcdn.com/image/fetch/$s_!2-wf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png 1272w, https://substackcdn.com/image/fetch/$s_!2-wf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39997f53-e9df-4e4e-9599-a0a1a02054ce_1700x942.png 1456w" sizes="auto, 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>Detecting face rectangles with Vision &#183; <a href="https://gist.github.com/jacobsapps/d0d14b8ec74d0150ff8a4f92303e3c27">View gist</a></em></figcaption></figure></div><p>This doesn&#8217;t perform any work to identify anyone. It&#8217;s just rectangles. </p><p>Underwhelming, perhaps, but no less important. It&#8217;s our first building block. </p><p>Now we can ignore the rest of the photo and work face-by-face instead of image-by-image. It also helps to do a simple post-processing step and discard faces too small or low-confidence to be useful, like the blurry lad in the back of your concert selfie.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KN1S!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KN1S!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png 424w, https://substackcdn.com/image/fetch/$s_!KN1S!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png 848w, https://substackcdn.com/image/fetch/$s_!KN1S!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png 1272w, https://substackcdn.com/image/fetch/$s_!KN1S!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!KN1S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png" width="1456" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:258439,&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://blog.jacobstechtavern.com/i/191349414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.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_!KN1S!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png 424w, https://substackcdn.com/image/fetch/$s_!KN1S!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png 848w, https://substackcdn.com/image/fetch/$s_!KN1S!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png 1272w, https://substackcdn.com/image/fetch/$s_!KN1S!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5521184b-5c04-4a0f-bd11-dfd051340777_1796x540.png 1456w" sizes="auto, 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>Filtering out tiny or low-confidence face detections &#183; <a href="https://gist.github.com/jacobsapps/5f15c0d72033648e042e8c15a87ed534">View gist</a></em></figcaption></figure></div><p>Step one is done. We have our list of rectangles associated with each image. Next, we convert the crops into&#8230; mathematics.</p><div><hr></div><h2><strong>Step #2: Computing Embeddings</strong></h2><p>What is an embedding? <s>A miserable little pile of secrets.</s>  It&#8217;s a fancy word for a vector, which is a fancy word for a list of numbers. A 512-item array of floats. If you like forensics-y metaphors, it&#8217;s a numerical fingerprint representing the image.</p><p>We create these embeddings by passing the pixels of a face image through the weights of our chosen face recognition model: pixels go in, embeddings go out.</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_!Pevi!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif 424w, https://substackcdn.com/image/fetch/$s_!Pevi!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif 848w, https://substackcdn.com/image/fetch/$s_!Pevi!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif 1272w, https://substackcdn.com/image/fetch/$s_!Pevi!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Pevi!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:18232957,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/191349414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Pevi!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif 424w, https://substackcdn.com/image/fetch/$s_!Pevi!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif 848w, https://substackcdn.com/image/fetch/$s_!Pevi!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif 1272w, https://substackcdn.com/image/fetch/$s_!Pevi!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9c6ec52-cd36-40b0-a101-6b05e445cf12_1680x1200.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><blockquote><p><strong>Aside: how are facial recognition models trained?<br></strong><em>As with most ML, you start with a huge labelled dataset, with many photos of each person across different poses, lighting, age, facial expression, and image quality, to ensure a model can learn that one person might be representable by a range of pixel values. From there, you train it <a href="https://www.youtube.com/watch?v=aircAruvnKk">the same way you train any neural network</a>.</em></p></blockquote><p>Now we know where to find faces, we are basically crunching geometry. Geometry like the spacing between your eyes, the position of your mouth, the shape of your skull. These properties tend to be constant between plastic surgeries.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-mSC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-mSC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-mSC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-mSC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-mSC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!-mSC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg" width="1206" height="714" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:714,&quot;width&quot;:1206,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:135564,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.jacobstechtavern.com/i/191349414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-mSC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-mSC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-mSC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-mSC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab772f51-6fc4-4f39-9a7e-ee79bd41bdb3_1206x714.jpeg 1456w" sizes="auto, 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="callout-block" data-callout="true"><p>If you&#8217;re a paid subscriber, now we get to the really fun bit:</p><ul><li><p>Computing embedding vectors from our faces</p></li><li><p>Placing these on a 512-dimensional hypersphere, </p></li><li><p>Running clustering algorithms to group faces accurately</p></li></ul><p>Paid members also get:</p><p>&#9875;&#65039; Access my <a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 50 paywalled articles</a> (including this one)<br>&#128640; Read <a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a> a month before anyone else<br>&#129525; Master concurrency with my <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a> and <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br>&#129489;&#8205;&#128640; Get a free copy of my new eBook, &#8220;<a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a>&#8221;<br>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=191349414&quot;,&quot;text&quot;:&quot;Get 14 day free trial&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=191349414"><span>Get 14 day free trial</span></a></p>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/apple-photos-and-face-recognition">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[UIKit for scroll performance!? Real engineers use Metal.]]></title><description><![CDATA[Actually hitting 120fps on a Photos app clone]]></description><link>https://blog.jacobstechtavern.com/p/real-engineers-use-metal</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/real-engineers-use-metal</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Tue, 23 Jun 2026 15:02:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!iUH3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iUH3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iUH3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!iUH3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!iUH3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!iUH3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iUH3!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:3474437,&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://blog.jacobstechtavern.com/i/202027554?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iUH3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!iUH3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!iUH3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!iUH3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa60766c0-42f5-4e88-91c6-6b56f5607954_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>Some days you wake up and think, <em>&#8220;Today I&#8217;m gonna be margarine. You won&#8217;t believe it&#8217;s not butter. I&#8217;ll ship a screen so silky smooth they&#8217;ll think it was powered by Morgan Freeman&#8217;s voice.&#8221;</em></p><p>Just me? Right.</p><p>Anyway. I woke up. Vengefully. Sherlocking. <em>Apple&#8217;s had it too easy for too long.</em> It&#8217;s time somebody Sherlocked them. Apple Photos. Everyone hates the redesign. I don&#8217;t even remember what it used to look like. But I can do better. </p><p>I have a lot of photos. I need to sprint to stand still; following the yearly upgrade cycle just to maintain enough storage space for all the pictures of my kids. SwiftUI <a href="https://blog.jacobstechtavern.com/p/swiftui-vs-uikit">will not be invited to this party</a>. I know enough about its performance when it comes to complex scrolling feeds.</p><p>UIKit is my go-to for smooth scrolling. I reached out for my trusty <strong>UICollectionView</strong>. My old friend. My legal guardian. My emergency contact.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aJp2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aJp2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png 424w, https://substackcdn.com/image/fetch/$s_!aJp2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png 848w, https://substackcdn.com/image/fetch/$s_!aJp2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png 1272w, https://substackcdn.com/image/fetch/$s_!aJp2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aJp2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png" width="1456" height="1086" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1086,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:651348,&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://blog.jacobstechtavern.com/i/202027554?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.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_!aJp2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png 424w, https://substackcdn.com/image/fetch/$s_!aJp2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png 848w, https://substackcdn.com/image/fetch/$s_!aJp2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.png 1272w, https://substackcdn.com/image/fetch/$s_!aJp2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a73a24c-cbf6-4492-9d99-d59a6f5ba9c8_2104x1570.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><p>Oh.</p><p>OH.</p><p>F*ck. </p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;4953d0d9-790e-4cbe-8d24-8c6d37aa85d6&quot;,&quot;duration&quot;:null}"></div><p>So&#8230; that wasn&#8217;t ideal.</p><p>What went wrong here? Why was my feed locked up, with more hitches and hangs than successful on-time frame renders?</p><p>And how can we do this properly?</p><p>Today, we&#8217;ll answer these questions, and demonstrate the right tool for the job: you hold onto UIKit scroll physics, but offload all the real rendering: <strong>GPU</strong>. <strong>Metal</strong>. <strong>120fps</strong>.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;d56f4319-858c-41f2-94d3-ab799350ed44&quot;,&quot;duration&quot;:null}"></div><div><hr></div><p>This article is packed full of so much knowledge, your email client will cut it off. Read on my website here.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/p/real-engineers-use-metal&quot;,&quot;text&quot;:&quot;Read on my website&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.jacobstechtavern.com/p/real-engineers-use-metal"><span>Read on my website</span></a></p><div><hr></div><h2><strong>UIKit Did Nothing Wrong</strong></h2><p>The UIKit trace, in my slightly montage-parody-flavoured video, was all hang, all the time. Even Spiegel would <a href="https://x.com/SkarredGhost/status/2067113818325708922">consider it a &#8220;potato&#8221;</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_!4P_0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png 424w, https://substackcdn.com/image/fetch/$s_!4P_0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png 848w, https://substackcdn.com/image/fetch/$s_!4P_0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png 1272w, https://substackcdn.com/image/fetch/$s_!4P_0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!4P_0!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png" width="1200" height="196.15384615384616" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:238,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:769760,&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://blog.jacobstechtavern.com/i/202027554?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4P_0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png 424w, https://substackcdn.com/image/fetch/$s_!4P_0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png 848w, https://substackcdn.com/image/fetch/$s_!4P_0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png 1272w, https://substackcdn.com/image/fetch/$s_!4P_0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ee91481-5903-4632-89a3-7b2adaa7ad41_3018x494.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>I&#8217;ll say anything to make my subscribers happy. I&#8217;ll <a href="https://blog.jacobstechtavern.com/p/swiftui-scroll-performance-the-120fps">praise SwiftUI</a>. I&#8217;ll <a href="https://blog.jacobstechtavern.com/p/swiftui-vs-uikit">praise UIKit</a>. I&#8217;ll <a href="https://blog.jacobstechtavern.com/p/the-year-swiftui-died">take down SwiftUI</a>. <a href="https://blog.jacobstechtavern.com/p/my-secret-plot-to-kill-swiftui">Twice</a>. I&#8217;ll tell you UIKit sucks. All year, I&#8217;ve been telling you UIKit is the one when it comes to performance, so why lie? </p><p>It&#8217;s not a lie. But engineering is about knowing the right tool for the job. <em>Especially</em> in the current landscape, where an agent can spit out plausible syntax like it&#8217;s nothing. </p><p>UIKit is really good, most of the time. With an infinitely scrolling feed of photos, or a collection view of, say, 5 columns, it&#8217;s solid. Here&#8217;s the same app, but magnified.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;d19785b0-52ee-4e68-86a2-758d3d8a926b&quot;,&quot;duration&quot;:null}"></div><p>This is great. On my entry-level iPhone 17, boasting a chonky 3nm A19 chip, it runs fine zoomed out to 15 columns as well. It&#8217;s only at 31 columns <em>(I copied the zoom levels from the Photos app)</em> where it falls apart. </p><p>This is <em>thousands</em> of visible <strong>UICollectionViewCell</strong>s onscreen. Placing a cell in the collection requires overhead from layout, reuse, PhotoKit callbacks, image view assignment, and Core Animation commits. Fine with a few columns, but it adds up.  </p><p>Instruments explained where all this work was happening: </p><ul><li><p>UICollectionView.layoutSubviews ate ~46% of main-thread samples.</p></li><li><p><strong>collectionView(_:cellForItemAt:)</strong> was in ~35% of samples.</p></li><li><p><strong>PHImageManager.requestImage(...)</strong> hit about ~24%.</p></li></ul><p>Eagle-eyed viewers can put away their <em>hmm, actually</em>: several main-thread samples are on the same call stack and so they can add to more than 100%.</p><p>My UIKit version wasn&#8217;t even that ambitious, lazing about at a paltry 60fps target, with a stonking 16.67ms frame budget. A hitch happens <a href="https://developer.apple.com/videos/play/tech-talks/10855/?time=93">anytime the UI render loop fails to prep a frame on time</a>. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZKNz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZKNz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png 424w, https://substackcdn.com/image/fetch/$s_!ZKNz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png 848w, https://substackcdn.com/image/fetch/$s_!ZKNz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png 1272w, https://substackcdn.com/image/fetch/$s_!ZKNz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZKNz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png" width="1456" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:562719,&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://blog.jacobstechtavern.com/i/202027554?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.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_!ZKNz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png 424w, https://substackcdn.com/image/fetch/$s_!ZKNz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png 848w, https://substackcdn.com/image/fetch/$s_!ZKNz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png 1272w, https://substackcdn.com/image/fetch/$s_!ZKNz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9293aa3e-2296-4b7c-b156-a020ff50c7a8_1888x996.png 1456w" sizes="auto, 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>Under 5ms of hitches per second is &#8220;good&#8221;. A billion hitches per second is &#8220;bad&#8221;. Zero is the target. So how do we get from this&#8230;</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;f9d9e158-dac0-4670-b66b-772d9178095a&quot;,&quot;duration&quot;:null}"></div><p>&#8230;to <em>this</em>:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;06038449-7cc8-4bbb-bee0-2f9078399b65&quot;,&quot;duration&quot;:null}"></div><p>It&#8217;s all about the Metal.</p><div class="callout-block" data-callout="true"><p>This article is exclusive to paid members. But. </p><p>If you want to scratch the itch, I have a few really good, related, free articles:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;9865b326-7b98-4474-bfdd-a220efa27106&quot;,&quot;caption&quot;:&quot;Subscribe to Jacob&#8217;s Tech Tavern for free to get ludicrously in-depth articles on iOS, Swift, tech, &amp; indie projects in your inbox every two weeks.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Metal in SwiftUI: How to Write Shaders &quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Master iOS. Boost your salary. Join 100,000 senior Swift devs learning advanced concurrency, SwiftUI, and iOS performance for 10 minutes a week. Sign up free today!&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2023-12-18T17:15:08.786Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F283962ef-2f47-490d-b837-0348a41690d5_854x480.gif&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/metal-in-swiftui-how-to-write-shaders&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:139833029,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:22,&quot;comment_count&quot;:3,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;2ed8e1d7-d422-4ca6-b38a-90a401c7400c&quot;,&quot;caption&quot;:&quot;I love SwiftUI; I hate SwiftUI.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Is SwiftUI finally as fast as UIKit in iOS 26?&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Master iOS. Boost your salary. Join 100,000 senior Swift devs learning advanced concurrency, SwiftUI, and iOS performance for 10 minutes a week. Sign up free today!&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2026-03-09T16:01:18.122Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!1Jn9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec517517-fbc1-4640-a984-33b4b9a0ade4_1680x1200.gif&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/swiftui-vs-uikit&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:184657452,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:34,&quot;comment_count&quot;:8,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;7a1ee294-bab4-4276-972a-367cdd157035&quot;,&quot;caption&quot;:&quot;I&#8217;ll save you 20 mins if you don&#8217;t want to read a whole blog post:&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Touch to Pixels: UI Pipeline Internals on iOS&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:126930235,&quot;name&quot;:&quot;Jacob Bartlett&quot;,&quot;bio&quot;:&quot;Master iOS. Boost your salary. Join 100,000 senior Swift devs learning advanced concurrency, SwiftUI, and iOS performance for 10 minutes a week. Sign up free today!&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!s80e!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feab1e065-dffc-4096-ad9e-826ddda8a6cd_1304x1304.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2026-05-13T15:03:01.053Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!m4bq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc761a8dc-045b-4a52-9225-ce99bec41fca_5917x2099.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.jacobstechtavern.com/p/ui-pipeline-internals&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:185299825,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:25,&quot;comment_count&quot;:1,&quot;publication_id&quot;:1376173,&quot;publication_name&quot;:&quot;Jacob&#8217;s Tech Tavern&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!LJp-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee402e25-4e20-4683-ba5f-20ca86eb2b43_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><p>Paid members get lots of nice things:</p><p>&#9875;&#65039; Access my <a href="https://blog.jacobstechtavern.com/t/elite-hacks">full library of 50 paywalled articles</a> (including this one)<br>&#128640; Read <a href="https://blog.jacobstechtavern.com/archive?sort=top">free articles</a> a month before anyone else<br>&#129525; Master concurrency with my <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-course">full course</a> and <a href="https://blog.jacobstechtavern.com/p/swift-concurrency-kata">advanced training</a><br>&#129489;&#8205;&#128640; Get a free copy of my new eBook, &#8220;<a href="https://payhip.com/b/tvoug">Land your iOS Tech Job</a>&#8221;<br>&#10084;&#65039;&#8205;&#129657; Support independent, sometimes funny, tech writing</p><p>The forbidden fruit tastes the sweetest, <em>non</em>? Especially when it&#8217;s free. Unlock your 2-week free trial today:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=202027554&quot;,&quot;text&quot;:&quot;Get 14 day free trial&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://blog.jacobstechtavern.com/subscribe?coupon=546d175b&amp;utm_content=202027554"><span>Get 14 day free trial</span></a></p></div>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/real-engineers-use-metal">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[How did Apple cut launch time by 30% in iOS 27?]]></title><description><![CDATA[An Instruments-flavoured optimisation deep-dive.]]></description><link>https://blog.jacobstechtavern.com/p/ios-27-launch-time</link><guid isPermaLink="false">https://blog.jacobstechtavern.com/p/ios-27-launch-time</guid><dc:creator><![CDATA[Jacob Bartlett]]></dc:creator><pubDate>Wed, 17 Jun 2026 15:02:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hl_q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hl_q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hl_q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!hl_q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!hl_q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!hl_q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hl_q!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png" width="1200" height="857.1428571428571" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1040,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:2360752,&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://blog.jacobstechtavern.com/i/201795024?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hl_q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png 424w, https://substackcdn.com/image/fetch/$s_!hl_q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png 848w, https://substackcdn.com/image/fetch/$s_!hl_q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!hl_q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F600c3d5d-6623-4591-b5f5-4de4cefb1a6c_1680x1200.png 1456w" sizes="100vw" fetchpriority="high"></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>Launch time is the most impactful metric you never optimise.</p><p>Say you&#8217;re WhatsApp.</p><p>You have a kajillion daily active users, except in America for some reason. </p><p>They may be warm-launching your app 10x a day. More. </p><p>A launch speed of 0.5 seconds, times 10, times a kajillion, means even a 1% improvement probably saves a few lifetimes&#8217; worth. I haven&#8217;t checked my maths, and I&#8217;m not going to.</p><p>Apple, helpfully, checked theirs. At <a href="https://developer.apple.com/videos/play/wwdc2019/423/?time=125">WWDC19&#8217;s Optimizing App Launch</a> session, they computed across billions of daily launches: shaving just 1ms off launch saves just about enough time each day to send a rocket to Mars.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XPpD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XPpD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png 424w, https://substackcdn.com/image/fetch/$s_!XPpD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png 848w, https://substackcdn.com/image/fetch/$s_!XPpD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png 1272w, https://substackcdn.com/image/fetch/$s_!XPpD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XPpD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png" width="1456" height="816" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:816,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2544898,&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://blog.jacobstechtavern.com/i/201795024?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.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_!XPpD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png 424w, https://substackcdn.com/image/fetch/$s_!XPpD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png 848w, https://substackcdn.com/image/fetch/$s_!XPpD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.png 1272w, https://substackcdn.com/image/fetch/$s_!XPpD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58446b90-7a6f-4566-bfcc-262dd96529bb_1852x1038.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><p>Apple had a <a href="https://blog.jacobstechtavern.com/p/wwdc-2026-roundup">&#8220;Snow Leopard&#8221; year</a> this WWDC. Less flashy features, less UI overhauls nobody asked for, more cold platform optimisations. They claimed to have added a 30% boost to app launch times in iOS 27. </p><p>I&#8217;ve chatted to you before about <a href="https://blog.jacobstechtavern.com/p/launch-optimisation">optimising launch time on a legacy app</a>, and <a href="https://blog.jacobstechtavern.com/p/xcode-organiser">tracing launch performance in production</a>. What I haven&#8217;t done is go all the way down. So today that&#8217;s what we&#8217;ll do:</p><ol><li><p>We&#8217;ll get to grips with the important stuff for devs to know about launch time.</p></li><li><p>We&#8217;ll trace iOS 26 and 27 improvements using Instruments.</p></li><li><p>We&#8217;ll clarify what Apple has actually done to improve things.</p></li></ol><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2><strong>Launch Theory</strong></h2><h4><strong>Flavours of Launch</strong></h4><p>There are three kinds of launch:</p><p><strong>&#128034; </strong>A <strong>cold launch </strong>is what you think of as a &#8220;launch&#8221;. The app process doesn&#8217;t exist, your binary is sitting in storage. <a href="https://asciiwwdc.com/2016/sessions/406">WWDC2016</a> explains that cold launches <em>mostly</em> only happen the first launch after install or restart. This is your first impression, though, so you probably want to worry about this. </p><p><strong>&#128007; </strong>A<strong> warm launch </strong>spawns a fresh process, but iOS caches binary memory pages aggressively in the kernel file cache, and dyld still holds onto a precomputed launch plan. tl;dr: the data is closer to where it needs to be.</p><p><strong>&#128005; </strong>There is no &#8220;<strong>hot launch</strong>&#8221;. It&#8217;s just a <strong>resume</strong>, foregrounding a backgrounded or suspended app that is already alive in process memory. </p><h4><strong>Launch Phases</strong></h4><p>There are 2 phases of launch to understand, based on when the <strong>main()</strong> function is called in your app process.</p><p>Knowing about <strong>pre-main</strong> launch steps is a great way to get ahead when your job interviewer is a jerk. First the kernel maps your Mach-O executable into process memory. Dyld, the dynamic linker, loads up the dynamic frameworks needed by your app, including your own code, third-party dependencies, and system frameworks like Foundation, UIKit, and Swift. </p><p>Address Space Layout Randomisation (ASLR) is a critical protection against memory corruption exploits, randomising the memory address of many of these frameworks in process memory at launch. Therefore <a href="https://developer.apple.com/videos/play/wwdc2022/110362/?time=1261">dyld applies &#8220;fixups&#8221;</a> to write symbol references to real memory addresses. </p><p>The <a href="https://blog.jacobstechtavern.com/p/reverse-engineer-private-frameworks">Swift runtime sets up</a> type metadata, protocol conformances, and generics. Finally, some of your own code might run: static initialisers and Objective-C <strong>load()</strong> methods. </p><p>This is a ton of work, but we will shortly see it visible in our instruments traces.</p><p>Nobody&#8217;s giving you a job for knowing about <strong>post-main</strong>. If you&#8217;re into CLIs, or hardcore <em>(like me)</em>, you might have your own explicit <strong>main()</strong> method.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">// main.swift

import UIKit

UIApplicationMain(
    CommandLine.argc,
    CommandLine.unsafeArgv,
    nil,
    NSStringFromClass(AppDelegate.self)
)</code></pre></div><p><strong><a href="https://developer.apple.com/documentation/uikit/about-the-app-launch-sequence?changes=_11">UIApplicationMain</a></strong> creates the app object, sets up the run loop, the app delegate, and ultimately calls <strong>application(_:willFinishLaunchingWithOptions:)</strong>. You know the rest, and if you don&#8217;t, my blog has a lot of solid iOS content.</p><p>Launch ends when your first frame renders. Optimising this side is standard engineering: profile, parallelise, and minimise the amount of work on the critical path before the user gets what they&#8217;re looking for. </p><div><hr></div><h2><strong>Reading the Launch Trace</strong></h2><p>The App Launch instrument gives a cool visual overview of the theory above.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cCeb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cCeb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png 424w, https://substackcdn.com/image/fetch/$s_!cCeb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png 848w, https://substackcdn.com/image/fetch/$s_!cCeb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png 1272w, https://substackcdn.com/image/fetch/$s_!cCeb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!cCeb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png" width="1456" height="521" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:521,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:857065,&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://blog.jacobstechtavern.com/i/201795024?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.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_!cCeb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png 424w, https://substackcdn.com/image/fetch/$s_!cCeb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png 848w, https://substackcdn.com/image/fetch/$s_!cCeb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png 1272w, https://substackcdn.com/image/fetch/$s_!cCeb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4ec55b4a-f3b5-4987-a3c6-14c1e0e0d937_2534x906.png 1456w" sizes="auto, 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><ul><li><p><strong>Launch Executable</strong> covers the full pre-main process.</p></li><li><p><strong>Validating Closure</strong> at the start is the tell for a warm launch, loading the launch plan <a href="https://github.com/apple-oss-distributions/dyld/blob/main/doc/PrebuiltLoaderSet_Policy.md">computed by dyld</a>. In cold launches, it&#8217;s <strong>Building Closure</strong>, lives at the end, and takes much longer. </p></li><li><p><strong>Map Image</strong> takes the actual file paths of your dynamic frameworks and <strong>mmap</strong>s them into your app process. This is where an overabundance of dynamic frameworks can slow down your launch. </p></li><li><p><strong>Apply Fixups</strong> wires these symbol references to actual memory addresses.</p></li><li><p><strong>ObjC Image Init</strong> makes classes <a href="https://blog.jacobstechtavern.com/p/objc-runtime-internals">visible to the Objective-C runtime</a>.</p></li><li><p><strong>Run Static Initializer</strong>&#8230; runs your static initialisers.</p></li><li><p><strong>dlopen</strong> handles library loads at runtime. <a href="https://blog.jacobstechtavern.com/i/148320646/dlopen">You can call this yourself!</a> </p></li></ul><p>Post-main, we can also see UIKit initialisation, <strong>didFinishLaunchingWithOptions</strong>, UIScene creation, then the declaration of victory: <strong>Foreground &#8212; Active*</strong>.</p><blockquote><p><em>*god I miss being allowed to use the em-dash</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iRwF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iRwF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png 424w, https://substackcdn.com/image/fetch/$s_!iRwF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png 848w, https://substackcdn.com/image/fetch/$s_!iRwF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png 1272w, https://substackcdn.com/image/fetch/$s_!iRwF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!iRwF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png" width="1456" height="381" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3209b477-ea29-4751-8c73-23db4619431e_2520x660.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:381,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:508538,&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://blog.jacobstechtavern.com/i/201795024?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.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_!iRwF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png 424w, https://substackcdn.com/image/fetch/$s_!iRwF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png 848w, https://substackcdn.com/image/fetch/$s_!iRwF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png 1272w, https://substackcdn.com/image/fetch/$s_!iRwF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3209b477-ea29-4751-8c73-23db4619431e_2520x660.png 1456w" sizes="auto, 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>Xcode 27 adds a cool flamegraph.  </p><p>On the Time Profile, hit the little blue button in the top-right, filter by your process, filter to the main thread, and you&#8217;ll see something like this: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4Ci3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4Ci3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png 424w, https://substackcdn.com/image/fetch/$s_!4Ci3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png 848w, https://substackcdn.com/image/fetch/$s_!4Ci3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png 1272w, https://substackcdn.com/image/fetch/$s_!4Ci3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!4Ci3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png" width="1456" height="746" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:746,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1701180,&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://blog.jacobstechtavern.com/i/201795024?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.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_!4Ci3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png 424w, https://substackcdn.com/image/fetch/$s_!4Ci3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png 848w, https://substackcdn.com/image/fetch/$s_!4Ci3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png 1272w, https://substackcdn.com/image/fetch/$s_!4Ci3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F18fd7797-7257-4694-b75e-c386c48ecf05_3008x1542.png 1456w" sizes="auto, 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>The % gives each segment&#8217;s share of CPU time in the given time window. To make things more useful for us, we can hide system libraries and see how our own code breaks down at launch*.</p><blockquote><p><em>*The flamegraph is also useful for non-launch performance work.</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7Xsf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7Xsf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png 424w, https://substackcdn.com/image/fetch/$s_!7Xsf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png 848w, https://substackcdn.com/image/fetch/$s_!7Xsf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png 1272w, https://substackcdn.com/image/fetch/$s_!7Xsf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7Xsf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png" width="1456" height="442" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:442,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:651558,&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://blog.jacobstechtavern.com/i/201795024?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.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_!7Xsf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png 424w, https://substackcdn.com/image/fetch/$s_!7Xsf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png 848w, https://substackcdn.com/image/fetch/$s_!7Xsf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png 1272w, https://substackcdn.com/image/fetch/$s_!7Xsf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa472d5ea-6a4b-4205-9491-fc6e3763d290_2524x766.png 1456w" sizes="auto, 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 class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.jacobstechtavern.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://blog.jacobstechtavern.com/subscribe?"><span>Subscribe now</span></a></p>
      <p>
          <a href="https://blog.jacobstechtavern.com/p/ios-27-launch-time">
              Read more
          </a>
      </p>
   ]]></content:encoded></item></channel></rss>