<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://swiftwithmajid.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://swiftwithmajid.com/" rel="alternate" type="text/html" /><updated>2026-06-16T16:49:40+00:00</updated><id>https://swiftwithmajid.com/feed.xml</id><title type="html">Swift with Majid</title><subtitle>Majid&apos;s blog about Swift development</subtitle><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><entry><title type="html">Swipe actions outside of List in SwiftUI</title><link href="https://swiftwithmajid.com/2026/06/16/swipe-actions-outside-of-list-in-swiftui/" rel="alternate" type="text/html" title="Swipe actions outside of List in SwiftUI" /><published>2026-06-16T00:00:00+00:00</published><updated>2026-06-16T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2026/06/16/swipe-actions-outside-of-list-in-swiftui</id><content type="html" xml:base="https://swiftwithmajid.com/2026/06/16/swipe-actions-outside-of-list-in-swiftui/"><![CDATA[<p>Swipe actions were a primary reason for using <em>List</em> in SwiftUI. As you may recall, I’ve mentioned several times that a scroll view paired with lazy stacks is the preferred approach in most scenarios, except when swipe actions are required.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<p>The <em>swipeActionsContainer</em> view modifier allows us to use swipe actions without <em>List</em>. This week, we will learn how to use <em>swipeActionsContainer</em> view modifier to attach swipe actions inside scroll view, lazy stacks, and even custom layouts.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">messages</span><span class="p">:</span> <span class="p">[</span><span class="kt">Message</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
        <span class="kt">Message</span><span class="p">(</span><span class="nv">content</span><span class="p">:</span> <span class="s">"Hello"</span><span class="p">),</span>
        <span class="kt">Message</span><span class="p">(</span><span class="nv">content</span><span class="p">:</span> <span class="s">"World"</span><span class="p">)</span>
    <span class="p">]</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">NavigationView</span> <span class="p">{</span>
            <span class="kt">List</span> <span class="p">{</span>
                <span class="kt">ForEach</span><span class="p">(</span><span class="n">messages</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="p">{</span> <span class="n">message</span> <span class="k">in</span>
                    <span class="kt">Text</span><span class="p">(</span><span class="n">message</span><span class="o">.</span><span class="n">content</span><span class="p">)</span>
                        <span class="o">.</span><span class="nf">swipeActions</span><span class="p">(</span><span class="nv">edge</span><span class="p">:</span> <span class="o">.</span><span class="n">trailing</span><span class="p">,</span> <span class="nv">allowsFullSwipe</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
                            <span class="kt">Button</span><span class="p">(</span><span class="s">"Delete"</span><span class="p">,</span> <span class="nv">role</span><span class="p">:</span> <span class="o">.</span><span class="n">destructive</span><span class="p">)</span> <span class="p">{</span>
                                <span class="n">messages</span><span class="o">.</span><span class="n">removeAll</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="n">message</span><span class="o">.</span><span class="n">id</span> <span class="p">}</span>
                            <span class="p">}</span>
                        <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">navigationTitle</span><span class="p">(</span><span class="s">"List"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As you can see in the example above, you can attach the <em>swipeActions</em> view modifier to the items of a <em>List</em> view to configure swipe actions. The <em>swipeActions</em> view modifier has been working only inside a <em>List</em> container. Fortunately, things have changed and now we can use the swipe actions with any container like a scroll view, lazy stacks, or even our custom layouts.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">ScrollView</span> <span class="p">{</span>
            <span class="kt">LazyVStack</span> <span class="p">{</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"Hello, World!"</span><span class="p">)</span>
                    <span class="o">.</span><span class="n">swipeActions</span> <span class="p">{</span>
                        <span class="kt">Button</span><span class="p">(</span><span class="nv">role</span><span class="p">:</span> <span class="o">.</span><span class="n">destructive</span><span class="p">)</span> <span class="p">{</span>
                            <span class="c1">// delete action</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">swipeActionsContainer</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here is another example where we attach the <em>swipeActions</em> view modifier to the items of the scroll view. All you need to make it work outside of <em>List</em> is enabling <em>swipeActions</em> by modifying the view using the <em>swipeActionsContainer</em> view modifier.</p>

<p>The <em>swipeActionsContainer</em> view modifier is crucial and works like a swipe actions enabler. It also controls a few things. For example, it enables only one row’s swipe actions to be revealed at a time. It tracks the scrolling events and dismisses any open actions. It also dismisses actions while tapping outside the active row.</p>

<p><em>List</em> doesn’t need <em>swipeActionsContainer</em> view modifier because it does that job automatically, but anything else than <em>List</em> needs the <em>swipeActionsContainer</em> view modifier attached.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">FlowLayout</span><span class="p">:</span> <span class="kt">Layout</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">sizeThatFits</span><span class="p">(</span>
        <span class="nv">proposal</span><span class="p">:</span> <span class="kt">ProposedViewSize</span><span class="p">,</span>
        <span class="nv">subviews</span><span class="p">:</span> <span class="kt">Subviews</span><span class="p">,</span>
        <span class="nv">cache</span><span class="p">:</span> <span class="nf">inout</span> <span class="p">()</span>
    <span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGSize</span> <span class="p">{</span>
        <span class="c1">// ...</span>
    <span class="p">}</span>
    
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">placeSubviews</span><span class="p">(</span>
        <span class="k">in</span> <span class="nv">bounds</span><span class="p">:</span> <span class="kt">CGRect</span><span class="p">,</span>
        <span class="nv">proposal</span><span class="p">:</span> <span class="kt">ProposedViewSize</span><span class="p">,</span>
        <span class="nv">subviews</span><span class="p">:</span> <span class="kt">Subviews</span><span class="p">,</span>
        <span class="nv">cache</span><span class="p">:</span> <span class="nf">inout</span> <span class="p">()</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="c1">// ...</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">FlowLayout</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="s">"Hello, World!"</span><span class="p">)</span>
                <span class="o">.</span><span class="n">swipeActions</span> <span class="p">{</span>
                    <span class="kt">Button</span><span class="p">(</span><span class="nv">role</span><span class="p">:</span> <span class="o">.</span><span class="n">destructive</span><span class="p">)</span> <span class="p">{</span>
                        
                    <span class="p">}</span>
                <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">swipeActionsContainer</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here is another example where we attach the <em>swipeActions</em> to the custom layout. I’m sure there is no reason to use flow layout with swipe actions, but you should know that you can use it with any custom layout as soon as you modify it with the <em>swipeActionsContainer</em> view modifier.</p>

<p>This is a small but very important improvement because it removes one more reason to reach for <em>List</em> when we don’t actually need it. We can keep full control over layout, styling, and performance while still providing native swipe interactions where they make sense. I hope you enjoy the post. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask your questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="Mastering SwiftUI views" /><summary type="html"><![CDATA[Swipe actions were a primary reason for using List in SwiftUI. As you may recall, I’ve mentioned several times that a scroll view paired with lazy stacks is the preferred approach in most scenarios, except when swipe actions are required.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/swipe-action.png" /><media:content medium="image" url="https://swiftwithmajid.com/public/swipe-action.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What is new in SwiftUI after WWDC26</title><link href="https://swiftwithmajid.com/2026/06/08/what-is-new-in-swiftui-after-wwdc26/" rel="alternate" type="text/html" title="What is new in SwiftUI after WWDC26" /><published>2026-06-08T00:00:00+00:00</published><updated>2026-06-08T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2026/06/08/what-is-new-in-swiftui-after-wwdc26</id><content type="html" xml:base="https://swiftwithmajid.com/2026/06/08/what-is-new-in-swiftui-after-wwdc26/"><![CDATA[<p>Platforms State of the Union has just been published, and we have a lot of new APIs to learn, explore, and use to build new features and apps. Let’s start with the most important framework for our apps. This week, we will look at what WWDC26 brings to the new iteration of SwiftUI.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<p>Liquid Glass design gained a few changes here and there, and fortunately, SwiftUI adopts many of them automatically. You don’t need to write additional code for things like glass tint, as they are automatically applied to your app’s user interface.</p>

<p>SwiftUI also introduces a new <em>prominent</em> tab role. You can use the <em>prominent</em> role for trailing-separated tabs, similar to search.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">TabView</span> <span class="p">{</span>
    <span class="kt">Tab</span><span class="p">(</span><span class="s">"insights"</span><span class="p">,</span> <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"chart.xyaxis.line"</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">NavigationStack</span> <span class="p">{</span>
            <span class="kt">InsightsFeatureView</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">navigationTitle</span><span class="p">(</span><span class="s">"insights"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="kt">Tab</span><span class="p">(</span><span class="s">"awareness"</span><span class="p">,</span> <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"text.book.closed"</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">NavigationStack</span> <span class="p">{</span>
            <span class="kt">AwarenessView</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">navigationTitle</span><span class="p">(</span><span class="s">"awareness"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="kt">Tab</span><span class="p">(</span><span class="s">"create"</span><span class="p">,</span> <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"pencil"</span><span class="p">,</span> <span class="nv">role</span><span class="p">:</span> <span class="o">.</span><span class="n">prominent</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">NavigationStack</span> <span class="p">{</span>
            <span class="kt">CreateFeatureView</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">navigationTitle</span><span class="p">(</span><span class="s">"create"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You can finally use the <em>swipeActions</em> view modifier with any view container, including <em>List</em>, <em>ScrollView</em>, lazy stacks, and even custom layouts. There is no need to use <em>List</em> only to support swipe actions anymore. All you need is the new <em>swipeActionsContainer</em> view modifier.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">ScrollView</span> <span class="p">{</span>
    <span class="kt">LazyVStack</span> <span class="p">{</span>
        <span class="kt">ForEach</span><span class="p">(</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span> <span class="n">item</span> <span class="k">in</span>
            <span class="kt">ItemRow</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
                <span class="o">.</span><span class="n">swipeActions</span> <span class="p">{</span>
                    <span class="kt">Button</span><span class="p">(</span><span class="s">"Delete"</span><span class="p">,</span> <span class="nv">role</span><span class="p">:</span> <span class="o">.</span><span class="n">destructive</span><span class="p">)</span> <span class="p">{</span>
                        <span class="nf">delete</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
                    <span class="p">}</span>
                <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">.</span><span class="nf">swipeActionsContainer</span><span class="p">()</span>
</code></pre></div></div>

<p>Reordering via drag and drop is easier than before with the new <em>reorderContainer</em> view modifier. It also works with <em>List</em>, <em>ScrollView</em>, lazy stacks, and custom layouts. Just apply the <em>reorderable</em> view modifier and handle the reordering action.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">landmarks</span><span class="p">:</span> <span class="p">[</span><span class="kt">Landmark</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>


    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span> <span class="p">{</span>
            <span class="kt">ForEach</span><span class="p">(</span><span class="n">landmarks</span><span class="p">)</span> <span class="p">{</span> <span class="n">landmark</span> <span class="k">in</span>
                <span class="kt">LandmarkView</span><span class="p">(</span><span class="n">landmark</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">reorderable</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">reorderContainer</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="kt">Landmark</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">difference</span> <span class="k">in</span>
            <span class="n">difference</span><span class="o">.</span><span class="nf">apply</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">landmarks</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Navigation gains a new cross-fade transition that we can use alongside zoom. We still can’t control navigation transitions manually, but now we have more built-in options: automatic, zoom, and cross-fade.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">showSheet</span> <span class="o">=</span> <span class="kc">false</span>


    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span> <span class="p">{</span>
            <span class="kt">Button</span><span class="p">(</span><span class="s">"Show Sheet"</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">showSheet</span> <span class="o">=</span> <span class="kc">true</span>
            <span class="p">}</span>
            <span class="o">.</span><span class="nf">sheet</span><span class="p">(</span><span class="nv">isPresented</span><span class="p">:</span> <span class="err">$</span><span class="n">showSheet</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"Sheet Content"</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">presentationDetents</span><span class="p">([</span><span class="o">.</span><span class="n">medium</span><span class="p">])</span>
                    <span class="o">.</span><span class="nf">navigationTransition</span><span class="p">(</span><span class="o">.</span><span class="n">crossFade</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><em>AsyncImage</em> also receives a performance improvement by introducing caching. You can even control the cache by configuring a custom <em>URLSession</em> with a specific cache size.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">let</span> <span class="nv">customCache</span> <span class="o">=</span> <span class="kt">URLCache</span><span class="p">(</span>
    <span class="nv">memoryCapacity</span><span class="p">:</span> <span class="mi">20</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">,</span>
    <span class="nv">diskCapacity</span><span class="p">:</span> <span class="mi">100</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">,</span>
    <span class="nv">directory</span><span class="p">:</span> <span class="kc">nil</span> <span class="c1">// Uses default system cache directory container</span>
<span class="p">)</span>

<span class="k">let</span> <span class="nv">configuration</span> <span class="o">=</span> <span class="kt">URLSessionConfiguration</span><span class="o">.</span><span class="k">default</span>
<span class="n">configuration</span><span class="o">.</span><span class="n">urlCache</span> <span class="o">=</span> <span class="n">customCache</span>
<span class="k">let</span> <span class="nv">session</span> <span class="o">=</span> <span class="kt">URLSession</span><span class="p">(</span><span class="nv">configuration</span><span class="p">:</span> <span class="n">configuration</span><span class="p">)</span>

<span class="kt">AsyncImage</span><span class="p">(</span>
    <span class="nv">request</span><span class="p">:</span> <span class="kt">URLRequest</span><span class="p">(</span>
        <span class="nv">url</span><span class="p">:</span> <span class="n">imageURL</span><span class="p">,</span>
        <span class="nv">cachePolicy</span><span class="p">:</span> <span class="o">.</span><span class="n">reloadIgnoringLocalCacheData</span>
    <span class="p">)</span>
<span class="p">)</span>
<span class="o">.</span><span class="nf">asyncImageURLSession</span><span class="p">(</span><span class="n">session</span><span class="p">)</span>
</code></pre></div></div>

<p>Apple introduced a collection of new toolbar view modifiers that allow us to control toolbar visibility more precisely. You can choose which toolbar items should have higher visibility priority on smaller devices, hide secondary items in the overflow menu, and pin an item to the trailing position of the top bar.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">RootView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">ContentView</span><span class="p">()</span>
            <span class="o">.</span><span class="n">toolbar</span> <span class="p">{</span>
                <span class="kt">ToolbarItem</span><span class="p">(</span><span class="nv">placement</span><span class="p">:</span> <span class="o">.</span><span class="n">topBarPinnedTrailing</span><span class="p">)</span> <span class="p">{</span>
                    <span class="kt">SecondaryControl</span><span class="p">()</span>
                <span class="p">}</span>
                
                <span class="kt">ToolbarItem</span> <span class="p">{</span>
                    <span class="kt">PrimaryControl</span><span class="p">()</span>
                <span class="p">}</span>
                <span class="o">.</span><span class="nf">visibilityPriority</span><span class="p">(</span><span class="o">.</span><span class="n">high</span><span class="p">)</span>
                
                <span class="kt">ToolbarOverflowMenu</span> <span class="p">{</span>
                    <span class="kt">Button</span><span class="p">(</span><span class="s">"Action 1"</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
                    <span class="kt">Button</span><span class="p">(</span><span class="s">"Action 2"</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Document-based apps get a refreshed look and feel, along with a performance boost. It looks like Xcode has started using these improvements, which might explain why we see so much work around document-based apps this year. And finally, Xcode introduces SwiftUI Specialist and What’s New in SwiftUI skills for agentic coding in Xcode.</p>

<p>SwiftUI continues to move toward a more flexible and system-integrated framework. This year’s updates may not completely change how we build apps, but they remove many small limitations around containers, navigation, toolbars, and document-based workflows.</p>

<p>These improvements make SwiftUI feel more mature and give us more reasons to rely on it for production apps. I hope you enjoy the post. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask your questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="Meta" /><summary type="html"><![CDATA[Platforms State of the Union has just been published, and we have a lot of new APIs to learn, explore, and use to build new features and apps. Let’s start with the most important framework for our apps. This week, we will look at what WWDC26 brings to the new iteration of SwiftUI.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/wwdc26.png" /><media:content medium="image" url="https://swiftwithmajid.com/public/wwdc26.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">WWDC26 wishes</title><link href="https://swiftwithmajid.com/2026/06/02/wwdc26-wishes/" rel="alternate" type="text/html" title="WWDC26 wishes" /><published>2026-06-02T00:00:00+00:00</published><updated>2026-06-02T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2026/06/02/wwdc26-wishes</id><content type="html" xml:base="https://swiftwithmajid.com/2026/06/02/wwdc26-wishes/"><![CDATA[<p>WWDC26 is just around the corner, and as always, it is the most exciting time of the year for Apple developers. Next week, Apple will show the future of its platforms, frameworks, and developer tools. Some announcements will probably be completely new, while others may finally address the gaps we have been feeling in our daily work.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<h4 id="foundation-models">Foundation Models</h4>
<p>I use Foundation Models almost in every personal project to build recommendation systems, coaching, awareness, and other features. One feature I really miss is the image input. It will allow us to build a new class of image classification apps with on-device image recognition and analysis. We can build with it everything from calorie tracking to bill parsing.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@Generable</span> <span class="kd">struct</span> <span class="kt">FoodInformation</span> <span class="p">{</span>
    <span class="kd">@Guide</span><span class="p">(</span><span class="nv">description</span><span class="p">:</span> <span class="s">"title"</span><span class="p">)</span> <span class="k">let</span> <span class="nv">title</span><span class="p">:</span> <span class="kt">String</span>
    <span class="kd">@Guide</span><span class="p">(</span><span class="nv">description</span><span class="p">:</span> <span class="s">"Estimated calories"</span><span class="p">)</span> <span class="k">let</span> <span class="nv">calories</span><span class="p">:</span> <span class="kt">Double</span>
    <span class="kd">@Guide</span><span class="p">(</span><span class="nv">description</span><span class="p">:</span> <span class="s">"Is provided food healthy?"</span><span class="p">)</span> <span class="k">let</span> <span class="nv">isHealthy</span><span class="p">:</span> <span class="kt">Bool</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">calculateMacros</span><span class="p">(</span><span class="k">for</span> <span class="nv">food</span><span class="p">:</span> <span class="kt">CGImage</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">FoodInformation</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">session</span> <span class="o">=</span> <span class="kt">LanguageModelSession</span><span class="p">(</span><span class="nv">instructions</span><span class="p">:</span> <span class="s">"Estimate macros for the food image"</span><span class="p">)</span>
    <span class="k">return</span> <span class="k">try</span> <span class="k">await</span> <span class="n">session</span><span class="o">.</span><span class="nf">respond</span><span class="p">(</span><span class="nv">generating</span><span class="p">:</span> <span class="kt">FoodInformation</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">for</span><span class="p">:</span> <span class="n">food</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We already have on-device image generation using the ImagePlayground framework. Why not have image analysis as part of Foundation Models? I think it is the most expected feature along with increased context size.</p>

<h4 id="custom-lazy-layouts">Custom lazy layouts</h4>
<p><em>Layout</em> protocol is great and allows us to build super custom layouts from flow layout to hexagonal layout. One thing it is really missing at the moment is the option to make it lazy, like <em>LazyVStack</em> or <em>LazyHStack</em>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">FlowLayout</span><span class="p">:</span> <span class="kt">Layout</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">spacing</span><span class="p">:</span> <span class="kt">CGFloat</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">rowSpacing</span><span class="p">:</span> <span class="kt">CGFloat</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">spacing</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span> <span class="nv">rowSpacing</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">8</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">spacing</span> <span class="o">=</span> <span class="n">spacing</span>
        <span class="k">self</span><span class="o">.</span><span class="n">rowSpacing</span> <span class="o">=</span> <span class="n">rowSpacing</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">sizeThatFits</span><span class="p">(</span>
        <span class="nv">proposal</span><span class="p">:</span> <span class="kt">ProposedViewSize</span><span class="p">,</span>
        <span class="nv">subviews</span><span class="p">:</span> <span class="kt">Subviews</span><span class="p">,</span>
        <span class="nv">cache</span><span class="p">:</span> <span class="nf">inout</span> <span class="p">()</span>
    <span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGSize</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">rows</span> <span class="o">=</span> <span class="nf">rows</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">subviews</span><span class="p">,</span> <span class="nv">maxWidth</span><span class="p">:</span> <span class="n">proposal</span><span class="o">.</span><span class="n">width</span> <span class="p">??</span> <span class="o">.</span><span class="n">greatestFiniteMagnitude</span><span class="p">)</span>
        <span class="k">let</span> <span class="nv">totalSpacing</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="nf">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">rows</span><span class="o">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">*</span> <span class="n">rowSpacing</span>
        <span class="k">let</span> <span class="nv">totalHeight</span> <span class="o">=</span> <span class="n">rows</span><span class="o">.</span><span class="nf">reduce</span><span class="p">(</span><span class="n">totalSpacing</span><span class="p">)</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">+</span> <span class="nv">$1</span><span class="o">.</span><span class="n">height</span> <span class="p">}</span>
        <span class="k">let</span> <span class="nv">maxRowWidth</span> <span class="o">=</span> <span class="n">rows</span><span class="o">.</span><span class="nf">map</span><span class="p">(\</span><span class="o">.</span><span class="n">width</span><span class="p">)</span><span class="o">.</span><span class="nf">max</span><span class="p">()</span> <span class="p">??</span> <span class="mi">0</span>

        <span class="k">return</span> <span class="kt">CGSize</span><span class="p">(</span><span class="nv">width</span><span class="p">:</span> <span class="n">maxRowWidth</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="n">totalHeight</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">placeSubviews</span><span class="p">(</span>
        <span class="k">in</span> <span class="nv">bounds</span><span class="p">:</span> <span class="kt">CGRect</span><span class="p">,</span>
        <span class="nv">proposal</span><span class="p">:</span> <span class="kt">ProposedViewSize</span><span class="p">,</span>
        <span class="nv">subviews</span><span class="p">:</span> <span class="kt">Subviews</span><span class="p">,</span>
        <span class="nv">cache</span><span class="p">:</span> <span class="nf">inout</span> <span class="p">()</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">rows</span> <span class="o">=</span> <span class="nf">rows</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">subviews</span><span class="p">,</span> <span class="nv">maxWidth</span><span class="p">:</span> <span class="n">bounds</span><span class="o">.</span><span class="n">width</span><span class="p">)</span>
        <span class="k">var</span> <span class="nv">y</span> <span class="o">=</span> <span class="n">bounds</span><span class="o">.</span><span class="n">minY</span>

        <span class="k">for</span> <span class="n">row</span> <span class="k">in</span> <span class="n">rows</span> <span class="p">{</span>
            <span class="k">var</span> <span class="nv">x</span> <span class="o">=</span> <span class="n">bounds</span><span class="o">.</span><span class="n">minX</span>

            <span class="k">for</span> <span class="n">item</span> <span class="k">in</span> <span class="n">row</span><span class="o">.</span><span class="n">items</span> <span class="p">{</span>
                <span class="n">subviews</span><span class="p">[</span><span class="n">item</span><span class="o">.</span><span class="n">index</span><span class="p">]</span><span class="o">.</span><span class="nf">place</span><span class="p">(</span>
                    <span class="nv">at</span><span class="p">:</span> <span class="kt">CGPoint</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="n">y</span> <span class="o">+</span> <span class="p">(</span><span class="n">row</span><span class="o">.</span><span class="n">height</span> <span class="o">-</span> <span class="n">item</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">),</span>
                    <span class="nv">proposal</span><span class="p">:</span> <span class="kt">ProposedViewSize</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">size</span><span class="p">)</span>
                <span class="p">)</span>
                <span class="n">x</span> <span class="o">+=</span> <span class="n">item</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="o">+</span> <span class="n">spacing</span>
            <span class="p">}</span>

            <span class="n">y</span> <span class="o">+=</span> <span class="n">row</span><span class="o">.</span><span class="n">height</span> <span class="o">+</span> <span class="n">rowSpacing</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It can be achieved by introducing the <em>LazyLayout</em> protocol, which extends the <em>Layout</em> protocol and provides us with some sort of control over laziness. Or maybe it can be implementation details that we don’t need to control but will give us laziness out of the box without any code changes.</p>

<h4 id="swiftui-recycling-view">SwiftUI Recycling View</h4>
<p>Another important feature I expect almost for three years is the recycling view in SwiftUI. At the moment, all views are displayed eagerly or lazily, but there is no reusing mechanism like in <em>UITableView</em> or <em>UICollectionView</em>. We still need to dive into UIKit from time to time and explore that opportunity behind the scenes.</p>

<p>You might say that most of the new devices are powered by new chips that have great performance, but even they need some kind of view reusing mechanism to display large collections of photos or mailboxes.</p>

<p>This’s not always possible to solve using lazy layouts. That’s why I still expect something similar to <em>RecyclerView</em> or maybe Apple can implement it behind the scenes using structured identity to identify similar views and recycle them inside the <em>ForEach</em> container.</p>

<h4 id="conclusion">Conclusion</h4>
<p>SwiftUI is already great for building interfaces quickly, and Foundation Models open a completely new direction for intelligent, private, on-device experiences. But there are still areas where we need to drop down to UIKit or build complicated workarounds. I hope you enjoy the post. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask your questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="Meta" /><summary type="html"><![CDATA[WWDC26 is just around the corner, and as always, it is the most exciting time of the year for Apple developers. Next week, Apple will show the future of its platforms, frameworks, and developer tools. Some announcements will probably be completely new, while others may finally address the gaps we have been feeling in our daily work.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/wwdc26.png" /><media:content medium="image" url="https://swiftwithmajid.com/public/wwdc26.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Swift Defer. Clean up before you leave.</title><link href="https://swiftwithmajid.com/2026/05/26/swift-defer-clean-up-before-you-leave/" rel="alternate" type="text/html" title="Swift Defer. Clean up before you leave." /><published>2026-05-26T00:00:00+00:00</published><updated>2026-05-26T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2026/05/26/swift-defer-clean-up-before-you-leave</id><content type="html" xml:base="https://swiftwithmajid.com/2026/05/26/swift-defer-clean-up-before-you-leave/"><![CDATA[<p>You may think about <strong>defer</strong> keyword as one of the most ambiguous language features in Swift, but it is very useful in some cases. You can use it deliberately, and it will give you safety. This week we will talk about some best practices of using <em>defer</em> in Swift.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<p><em>Defer</em> keyword in Swift allows you to run a block of code at the end of the current scope. What does current scope mean? Usually, it is the nearest curly braces pair. Let’s take a look at a few examples.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// start parent scope</span>

    <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">...</span><span class="mi">10</span> <span class="p">{</span>
        <span class="c1">// start scope i</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
        <span class="c1">// end scope i</span>
    <span class="p">}</span>
    <span class="c1">// end parent scope</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As you can see in the example above, every open curly brace defines a new scope. It might be a function, for loop, calculated property, etc. So, the <em>defer</em> keyword defined inside a particular scope affects only this scope and runs just before the end of the scope.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// start parent scope</span>
    <span class="k">defer</span> <span class="p">{</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"end parent scope"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">...</span><span class="mi">10</span> <span class="p">{</span>
        <span class="c1">// start scope i</span>
        <span class="k">defer</span> <span class="p">{</span>
            <span class="nf">print</span><span class="p">(</span><span class="s">"end scope, </span><span class="se">\(</span><span class="n">i</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="nf">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
        <span class="c1">// end scope i</span>
    <span class="p">}</span>
    <span class="c1">// end parent scope, print("end parent scope") runs here</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here I try to show where <em>defer</em> blocks should execute. Every closing curly brace is closing some scope, and this is exactly the place where <em>defer</em> blocks run.</p>

<p>So, you can place <em>defer</em> blocks anywhere in the scope, but they will run at the end of the scope. Why might we need so ambiguous behaviour? We used to read and understand the code line by line, but <em>defer</em> blocks are different. I’m sure you don’t need to use <em>defer</em> blocks often, but there are some cases where they shine.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">_</span> <span class="nv">epg</span><span class="p">:</span> <span class="kt">URL</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">uuid</span> <span class="o">=</span> <span class="kt">UUID</span><span class="p">()</span>
    <span class="k">let</span> <span class="p">(</span><span class="nv">data</span><span class="p">,</span> <span class="nv">_</span><span class="p">)</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="kt">URLSession</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="nf">data</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">epg</span><span class="p">)</span>
    <span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="kt">URL</span><span class="o">.</span><span class="n">temporaryDirectory</span><span class="o">.</span><span class="nf">appending</span><span class="p">(</span><span class="nv">path</span><span class="p">:</span> <span class="n">uuid</span><span class="o">.</span><span class="n">uuidString</span><span class="p">)</span>
    <span class="k">try</span> <span class="n">data</span><span class="o">.</span><span class="nf">write</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>

    <span class="k">guard</span> <span class="k">let</span> <span class="nv">parser</span> <span class="o">=</span> <span class="kt">XMLParser</span><span class="p">(</span><span class="nv">contentsOf</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span>
    <span class="p">}</span>

    <span class="n">parser</span><span class="o">.</span><span class="nf">parse</span><span class="p">()</span>

    <span class="k">try</span> <span class="kt">FileManager</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">removeItem</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As you can see, we create a file in the temporary directory, and we should delete it as soon as we finish work with it. You can create that file and easily forget to remove it after all. Believe me, that’s happened more often than you might think.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">_</span> <span class="nv">epg</span><span class="p">:</span> <span class="kt">URL</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">uuid</span> <span class="o">=</span> <span class="kt">UUID</span><span class="p">()</span>
    <span class="k">let</span> <span class="p">(</span><span class="nv">data</span><span class="p">,</span> <span class="nv">_</span><span class="p">)</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="kt">URLSession</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="nf">data</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">epg</span><span class="p">)</span>
    <span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="kt">URL</span><span class="o">.</span><span class="n">temporaryDirectory</span><span class="o">.</span><span class="nf">appending</span><span class="p">(</span><span class="nv">path</span><span class="p">:</span> <span class="n">uuid</span><span class="o">.</span><span class="n">uuidString</span><span class="p">)</span>
    <span class="k">try</span> <span class="n">data</span><span class="o">.</span><span class="nf">write</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
    
    <span class="k">defer</span> <span class="p">{</span>
        <span class="k">try</span><span class="p">?</span> <span class="kt">FileManager</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">removeItem</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">guard</span> <span class="k">let</span> <span class="nv">parser</span> <span class="o">=</span> <span class="kt">XMLParser</span><span class="p">(</span><span class="nv">contentsOf</span><span class="p">:</span> <span class="n">url</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">return</span>
    <span class="p">}</span>

    <span class="n">parser</span><span class="o">.</span><span class="nf">parse</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You can create a file and define a <em>defer</em> block that deletes it. You can place it right below the file creation, but it will execute at the end of the scope.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="k">await</span> <span class="n">handler</span> <span class="k">in</span> <span class="k">await</span> <span class="n">health</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span>
    <span class="nv">types</span><span class="p">:</span> <span class="p">[</span>
        <span class="o">.</span><span class="nf">workoutType</span><span class="p">(),</span>
        <span class="kt">HKQuantityType</span><span class="o">.</span><span class="n">hrv</span><span class="p">,</span>
        <span class="kt">HKQuantityType</span><span class="o">.</span><span class="n">heartRate</span><span class="p">,</span>
        <span class="kt">HKCategoryType</span><span class="o">.</span><span class="n">sleep</span><span class="p">,</span>
        <span class="kt">HKCategoryType</span><span class="o">.</span><span class="n">mindful</span>
    <span class="p">]</span>
<span class="p">)</span> <span class="p">{</span>
    <span class="k">defer</span> <span class="p">{</span> <span class="nf">handler</span><span class="p">()</span> <span class="p">}</span>
    
    <span class="k">if</span> <span class="n">handler</span><span class="o">.</span><span class="n">types</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="kt">HKQuantityType</span><span class="o">.</span><span class="n">heartRate</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">await</span> <span class="nf">processHeartRate</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let me show you another example. Here we use HealthKit observation API, that requires you to call a completion handler as soon as you finish your processing. This is another interesting usage of a <em>defer</em> block, because you define it at the start of the scope, but it will run in the end right after you finish all your processing.</p>

<p>The <em>defer</em> keyword is not something you need every day, but it is a great tool for making cleanup code safer and easier to reason about. Whenever you create a temporary resource, acquire a lock, change some state, or receive a completion handler that must be called later, <em>defer</em> allows you to describe the cleanup right next to the setup. I hope you enjoy the post. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask your questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="Swift Language Features" /><summary type="html"><![CDATA[You may think about defer keyword as one of the most ambiguous language features in Swift, but it is very useful in some cases. You can use it deliberately, and it will give you safety. This week we will talk about some best practices of using defer in Swift.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/swift.png" /><media:content medium="image" url="https://swiftwithmajid.com/public/swift.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Deprecating your own convenience API</title><link href="https://swiftwithmajid.com/2026/05/19/deprecating-your-own-convenience-api/" rel="alternate" type="text/html" title="Deprecating your own convenience API" /><published>2026-05-19T00:00:00+00:00</published><updated>2026-05-19T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2026/05/19/deprecating-your-own-convenience-api</id><content type="html" xml:base="https://swiftwithmajid.com/2026/05/19/deprecating-your-own-convenience-api/"><![CDATA[<p>Almost after every major update of iOS, we got new APIs that we use on the most recent platform but can’t use on the previous one. Usually, I solve this kind of thing by introducing my own convenience code that runs new APIs on the available versions and my custom implementation or stubs on old platform versions.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<p>Usually, my apps support two of the most recent platform versions, and it is easy to maintain. But now, we have iOS 26 and iOS 18, and they differ a lot in many small details. For example, in iOS 26, toolbar items are displayed as SF Symbols or any other similar image, but on iOS 18, we used to display text-based buttons. It is easy to solve, but it creates code that will be dead in a year when the new iOS version arrives.</p>

<p>So, you should keep in mind every custom type or function you build to cover functionality on older platforms, because you might need to delete them in a year as soon as you bump the minimal platform version. Or, you can make the compiler remind you about that code. This week, we will talk about a way to make the compiler help us in identifying dead code in our codebase.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">NavigationStack</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">navigationTitle</span><span class="p">(</span><span class="s">"Content"</span><span class="p">)</span>
                <span class="o">.</span><span class="n">toolbar</span> <span class="p">{</span>
                    <span class="kt">ToolbarItem</span><span class="p">(</span><span class="nv">placement</span><span class="p">:</span> <span class="o">.</span><span class="n">cancellationAction</span><span class="p">)</span> <span class="p">{</span>
                        <span class="kt">Button</span><span class="p">(</span><span class="s">"cancel"</span><span class="p">,</span> <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"xmark"</span><span class="p">)</span> <span class="p">{}</span>
                    <span class="p">}</span>

                    <span class="kt">ToolbarItem</span><span class="p">(</span><span class="nv">placement</span><span class="p">:</span> <span class="o">.</span><span class="n">confirmationAction</span><span class="p">)</span> <span class="p">{</span>
                        <span class="kt">Button</span><span class="p">(</span><span class="s">"done"</span><span class="p">,</span> <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"checkmark"</span><span class="p">)</span> <span class="p">{}</span>
                    <span class="p">}</span>
                <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s take a look at a simple example above. We set up two toolbar items. We want to achieve a symbol-only look and feel on iOS 26 and a text-only look and feel on iOS 18. For these particular cases, I’ve introduced the <em>ToolbarLabelStyle</em> type.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">ToolbarLabelStyle</span><span class="p">:</span> <span class="kt">LabelStyle</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">makeBody</span><span class="p">(</span><span class="nv">configuration</span><span class="p">:</span> <span class="kt">Configuration</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">if</span> <span class="kd">#available(iOS 26, *)</span> <span class="p">{</span>
            <span class="kt">Label</span><span class="p">(</span><span class="n">configuration</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">labelStyle</span><span class="p">(</span><span class="o">.</span><span class="n">iconOnly</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="kt">Label</span><span class="p">(</span><span class="n">configuration</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">labelStyle</span><span class="p">(</span><span class="o">.</span><span class="n">titleOnly</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">LabelStyle</span> <span class="k">where</span> <span class="k">Self</span> <span class="o">==</span> <span class="kt">ToolbarLabelStyle</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">toolbar</span><span class="p">:</span> <span class="k">Self</span> <span class="p">{</span> <span class="o">.</span><span class="nf">init</span><span class="p">()</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">NavigationStack</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">navigationTitle</span><span class="p">(</span><span class="s">"Content"</span><span class="p">)</span>
                <span class="o">.</span><span class="n">toolbar</span> <span class="p">{</span>
                    <span class="kt">ToolbarItem</span><span class="p">(</span><span class="nv">placement</span><span class="p">:</span> <span class="o">.</span><span class="n">cancellationAction</span><span class="p">)</span> <span class="p">{</span>
                        <span class="kt">Button</span><span class="p">(</span><span class="s">"cancel"</span><span class="p">,</span> <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"xmark"</span><span class="p">)</span> <span class="p">{}</span>
                    <span class="p">}</span>

                    <span class="kt">ToolbarItem</span><span class="p">(</span><span class="nv">placement</span><span class="p">:</span> <span class="o">.</span><span class="n">confirmationAction</span><span class="p">)</span> <span class="p">{</span>
                        <span class="kt">Button</span><span class="p">(</span><span class="s">"done"</span><span class="p">,</span> <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"checkmark"</span><span class="p">)</span> <span class="p">{}</span>
                    <span class="p">}</span>
                <span class="p">}</span>
                <span class="o">.</span><span class="nf">labelStyle</span><span class="p">(</span><span class="o">.</span><span class="n">toolbar</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As you can see in the example above, we easily solve this by introducing <em>ToolbarLabelStyle</em> type. It checks the availability of the platform and applies the correct styling to our labels. The code looks and feels very natural, but it will become dead code when I bump the target version to iOS 26. How to find this type of code in my codebase? It might be in so many places where I use similar solutions.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">LabelStyle</span> <span class="k">where</span> <span class="k">Self</span> <span class="o">==</span> <span class="kt">ToolbarLabelStyle</span> <span class="p">{</span>
    <span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span><span class="p">,</span> <span class="nv">deprecated</span><span class="p">:</span> <span class="mi">26</span><span class="p">,</span> <span class="nv">obsoleted</span><span class="p">:</span> <span class="mi">27</span><span class="p">,</span> <span class="nv">message</span><span class="p">:</span> <span class="s">"You don't need .toolbar anymore"</span><span class="p">)</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">toolbar</span><span class="p">:</span> <span class="k">Self</span> <span class="p">{</span> <span class="o">.</span><span class="nf">init</span><span class="p">()</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Fortunately, we can use <em>availability</em> annotations. We annotate our <em>toolbar</em> property with the <em>availability</em> annotation, which deprecates and obsoletes the usage of the <em>toolbar</em> property. You are curious about what it means?</p>

<blockquote>
  <p>To learn more about availability annotation, take a look at my <a href="/2023/05/17/api-availability-in-swift/">“API availability in Swift”</a> post.</p>
</blockquote>

<p>As soon as you bump the target of your app to iOS 26, all the usage of the <em>toolbar</em> property will be marked as warnings by the compiler with the message we put in the annotation. Whenever you bump the target to iOS 27 (in the future), the compiler will produce an error because this code is already obsolete.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">LabelStyle</span> <span class="k">where</span> <span class="k">Self</span> <span class="o">==</span> <span class="kt">ToolbarLabelStyle</span> <span class="p">{</span>
    <span class="kd">@available</span><span class="p">(</span><span class="n">iOS</span><span class="p">,</span> <span class="nv">obsoleted</span><span class="p">:</span> <span class="mi">26</span><span class="p">,</span> <span class="nv">message</span><span class="p">:</span> <span class="s">"You don't need .toolbar anymore"</span><span class="p">)</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">toolbar</span><span class="p">:</span> <span class="k">Self</span> <span class="p">{</span> <span class="o">.</span><span class="nf">init</span><span class="p">()</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You can be more aggressive and instead of deprecating your code, make it obsolete for iOS 26. In this case, you will get compiler errors instead of warnings. Sometimes, it might be blocking you from some work, so I highly encourage you to deprecate first, then obsolete. But you should always take care of compiler warnings to not accumulate a technical debt.</p>

<p>I really like this approach because it keeps the codebase honest. We can freely build ergonomic wrappers for older platforms while still having a clear path to remove them later. And the best part is that the compiler does all the reminding for us. I hope you enjoy the post. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask your questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="Swift Language Features" /><summary type="html"><![CDATA[Almost after every major update of iOS, we got new APIs that we use on the most recent platform but can’t use on the previous one. Usually, I solve this kind of thing by introducing my own convenience code that runs new APIs on the available versions and my custom implementation or stubs on old platform versions.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/swift.png" /><media:content medium="image" url="https://swiftwithmajid.com/public/swift.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Building List replacement in SwiftUI</title><link href="https://swiftwithmajid.com/2026/04/06/building-list-replacement-in-swiftui/" rel="alternate" type="text/html" title="Building List replacement in SwiftUI" /><published>2026-04-06T00:00:00+00:00</published><updated>2026-04-06T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2026/04/06/building-list-replacement-in-swiftui</id><content type="html" xml:base="https://swiftwithmajid.com/2026/04/06/building-list-replacement-in-swiftui/"><![CDATA[<p>Whenever you consider creating a scrollable screen in SwiftUI, you might think of using a <em>List</em>. However, it’s not always the best choice. Lists are great for displaying uniform data. For anything else, a <em>ScrollView</em> with a lazy stack is almost always the best option. This week, we will learn how to build a custom scrollable container in SwiftUI with precise control of look and feel.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<p>First of all, I should mention that over the last few years SwiftUI has significantly improved the performance of <em>ScrollView</em> in pairs with lazy stacks. So, if you are not displaying hundreds of thousands of uniform data like mailboxes or to-do lists, the <em>ScrollView</em> is a way to go.</p>

<p><img src="/public/design-system.jpeg" alt="cardiobot-new-design" /></p>

<p>You can see 4 screenshots here. The first two of them represent the current state of my <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-monitor/id1149412984?uo=4">CardioBot</a> app. The next two screenshots are the result I want to achieve. As you might notice, I use a standard <em>List</em> at the very moment, and I really like how the app looks and feels now. But I’ve decided to reconsider my UI. I want to keep it simple and recognizable for iPhone users, but I would like to make the UI more fancy.</p>

<p>As you can see, my app displays different health metrics. It is not a uniform data set, and it doesn’t make any sense to use the <em>List</em> for recycling cells. I use multiple card types like <em>HeroCard</em>, <em>TintedCard</em>, and <em>RegularCard</em>. I can achieve a similar look and feel using List and list-specific view modifiers like <em>listRowBackground</em>, <em>listItemTint</em>, and <em>listRowInsets</em>. Unfortunately, these list-specific view modifiers don’t work outside of the <em>List</em> view, which requires additional styling outside the <em>List</em>.</p>

<p>Fortunately, SwiftUI introduced Container View APIs that we can use to build a List-replacement. Container View APIs allow us to decompose SwiftUI views, apply some changes, and compose again. So, we can use the Container View APIs to build reusable container views like <em>List</em>, <em>Form</em>, or anything super custom.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">ScrollingSurface</span><span class="o">&lt;</span><span class="kt">Content</span><span class="p">:</span> <span class="kt">View</span><span class="o">&gt;</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">enum</span> <span class="kt">Direction</span> <span class="p">{</span>
        <span class="k">case</span> <span class="nf">vertical</span><span class="p">(</span><span class="kt">HorizontalAlignment</span><span class="p">)</span>
        <span class="k">case</span> <span class="nf">horizontal</span><span class="p">(</span><span class="kt">VerticalAlignment</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">let</span> <span class="nv">direction</span><span class="p">:</span> <span class="kt">Direction</span>
    <span class="k">let</span> <span class="nv">spacing</span><span class="p">:</span> <span class="kt">CGFloat</span><span class="p">?</span>
    <span class="k">let</span> <span class="nv">content</span><span class="p">:</span> <span class="kt">Content</span>

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span>
        <span class="n">_</span> <span class="nv">direction</span><span class="p">:</span> <span class="kt">Direction</span> <span class="o">=</span> <span class="o">.</span><span class="nf">vertical</span><span class="p">(</span><span class="o">.</span><span class="n">leading</span><span class="p">),</span>
        <span class="nv">spacing</span><span class="p">:</span> <span class="kt">CGFloat</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
        <span class="kd">@ViewBuilder</span> <span class="nv">content</span><span class="p">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Content</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">spacing</span> <span class="o">=</span> <span class="n">spacing</span>
        <span class="k">self</span><span class="o">.</span><span class="n">direction</span> <span class="o">=</span> <span class="n">direction</span>
        <span class="k">self</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="nf">content</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">switch</span> <span class="n">direction</span> <span class="p">{</span>
        <span class="k">case</span> <span class="o">.</span><span class="nf">horizontal</span><span class="p">(</span><span class="k">let</span> <span class="nv">alignment</span><span class="p">):</span>
            <span class="kt">ScrollView</span><span class="p">(</span><span class="o">.</span><span class="n">horizontal</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">LazyHStack</span><span class="p">(</span><span class="nv">alignment</span><span class="p">:</span> <span class="n">alignment</span><span class="p">,</span> <span class="nv">spacing</span><span class="p">:</span> <span class="n">spacing</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">content</span>
                <span class="p">}</span>
                <span class="o">.</span><span class="nf">scrollTargetLayout</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
            <span class="p">}</span>
        <span class="k">case</span> <span class="o">.</span><span class="nf">vertical</span><span class="p">(</span><span class="k">let</span> <span class="nv">alignment</span><span class="p">):</span>
            <span class="kt">ScrollView</span><span class="p">(</span><span class="o">.</span><span class="n">vertical</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">LazyVStack</span><span class="p">(</span><span class="nv">alignment</span><span class="p">:</span> <span class="n">alignment</span><span class="p">,</span> <span class="nv">spacing</span><span class="p">:</span> <span class="n">spacing</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">content</span>
                <span class="p">}</span>
                <span class="o">.</span><span class="nf">scrollTargetLayout</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Every screen in my app uses <em>ScrollView</em> with a lazy stack. So, I’ve created the <em>ScrollingSurface</em> type. As you can see, it is a simple wrapper around the <em>ScrollView</em> and <em>LazyVStack</em> or <em>LazyHStack</em> depending on the chosen direction. I will use the <em>ScrollingSurface</em> type as the root view on every screen of my app.</p>

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

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="kd">@ViewBuilder</span> <span class="nv">content</span><span class="p">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Content</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="nf">content</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">Group</span><span class="p">(</span><span class="nv">subviews</span><span class="p">:</span> <span class="n">content</span><span class="p">)</span> <span class="p">{</span> <span class="n">subviews</span> <span class="k">in</span>
            <span class="k">if</span> <span class="o">!</span><span class="n">subviews</span><span class="o">.</span><span class="n">isEmpty</span> <span class="p">{</span>
                <span class="kt">VStack</span><span class="p">(</span><span class="nv">alignment</span><span class="p">:</span> <span class="o">.</span><span class="n">leading</span><span class="p">)</span> <span class="p">{</span>
                    <span class="kt">ForEach</span><span class="p">(</span><span class="n">subviews</span><span class="p">)</span> <span class="p">{</span> <span class="n">subview</span> <span class="k">in</span>
                        <span class="n">subview</span>

                        <span class="k">if</span> <span class="n">subviews</span><span class="o">.</span><span class="n">last</span><span class="p">?</span><span class="o">.</span><span class="n">id</span> <span class="o">!=</span> <span class="n">subview</span><span class="o">.</span><span class="n">id</span> <span class="p">{</span>
                            <span class="kt">Divider</span><span class="p">()</span>
                                <span class="o">.</span><span class="nf">padding</span><span class="p">(</span><span class="o">.</span><span class="n">vertical</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                <span class="p">}</span>
                <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
                <span class="o">.</span><span class="nf">frame</span><span class="p">(</span><span class="nv">maxWidth</span><span class="p">:</span> <span class="o">.</span><span class="n">infinity</span><span class="p">,</span> <span class="nv">alignment</span><span class="p">:</span> <span class="o">.</span><span class="n">topLeading</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">background</span><span class="p">(</span><span class="o">.</span><span class="n">regularMaterial</span><span class="p">,</span> <span class="nv">in</span><span class="p">:</span> <span class="kt">RoundedRectangle</span><span class="p">(</span><span class="nv">cornerRadius</span><span class="p">:</span> <span class="mi">32</span><span class="p">))</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Next most important primitive for UI is the <em>DividedCard</em> type. As you can see, it uses <strong>Group(subviews:)</strong> which is a part of SwiftUI Container View API. This initializer of the <em>Group</em> type allows us to decompose the view passed with a <em>ViewBuilder</em> closure.</p>

<blockquote>
  <p>To learn more about Container View APIs in SwiftUI, take a look at my dedicated <a href="https://swiftwithmajid.com/2024/09/24/mastering-container-views-in-swiftui-basics/">“Mastering container views in SwiftUI. Basics.”</a> post.</p>
</blockquote>

<p>In the <em>DividedCard</em> view, we decompose the passed view and add the divider after each child view. In the end, we wrap the whole view with a background with rounded corners to make it feel like a card.</p>

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

    <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="kd">@ViewBuilder</span> <span class="nv">content</span><span class="p">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Content</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="nf">content</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">public</span> <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">ForEach</span><span class="p">(</span><span class="nv">sections</span><span class="p">:</span> <span class="n">content</span><span class="p">)</span> <span class="p">{</span> <span class="n">section</span> <span class="k">in</span>
            <span class="k">if</span> <span class="o">!</span><span class="n">section</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="n">isEmpty</span> <span class="p">{</span>
                <span class="n">section</span><span class="o">.</span><span class="n">header</span><span class="o">.</span><span class="nf">padding</span><span class="p">(</span><span class="o">.</span><span class="n">top</span><span class="p">)</span>
                <span class="n">section</span><span class="o">.</span><span class="n">content</span>
                <span class="n">section</span><span class="o">.</span><span class="n">footer</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Another interesting UI primitive I built is the <em>SectionedSurface</em>. It uses <strong>ForEach(sections:)</strong> which allows us to extract all the sections from the passed view and filter out the sections without content and add some paddings to section headers.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">NavigationButtonStyle</span><span class="p">:</span> <span class="kt">ButtonStyle</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">makeBody</span><span class="p">(</span><span class="nv">configuration</span><span class="p">:</span> <span class="kt">Configuration</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">HStack</span> <span class="p">{</span>
            <span class="n">configuration</span><span class="o">.</span><span class="n">label</span>
                <span class="o">.</span><span class="nf">opacity</span><span class="p">(</span><span class="n">configuration</span><span class="o">.</span><span class="n">isPressed</span> <span class="p">?</span> <span class="mf">0.7</span> <span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
            <span class="kt">Spacer</span><span class="p">()</span>
            <span class="kt">Image</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="s">"chevron.right"</span><span class="p">)</span>
                <span class="o">.</span><span class="nf">foregroundStyle</span><span class="p">(</span><span class="o">.</span><span class="n">tertiary</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">contentShape</span><span class="p">(</span><span class="o">.</span><span class="n">rect</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">ButtonStyle</span> <span class="k">where</span> <span class="k">Self</span> <span class="o">==</span> <span class="kt">NavigationButtonStyle</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="k">var</span> <span class="nv">navigation</span><span class="p">:</span> <span class="k">Self</span> <span class="p">{</span> <span class="o">.</span><span class="nf">init</span><span class="p">()</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>One thing you can miss from using <em>List</em> view in SwiftUI is the styling of a <em>NavigationLink</em>. <em>List</em> automatically adds the chevron on the trailing edge of a <em>NavigationLink</em>. Fortunately, we can achieve that using a custom button style.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">SummaryView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">summary</span><span class="p">:</span> <span class="kt">SummaryStore</span>
    
    <span class="kd">public</span> <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">ScrollingSurface</span> <span class="p">{</span>
            <span class="kt">SectionedSurface</span> <span class="p">{</span>
                <span class="n">coachSection</span>
                <span class="n">activitySection</span>
                <span class="n">recoverySection</span>
                <span class="n">vitalsSection</span>
                <span class="n">heartRateSection</span>
                <span class="n">alcoholicBeveragesSection</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">buttonStyle</span><span class="p">(</span><span class="o">.</span><span class="n">navigation</span><span class="p">)</span>
    <span class="p">}</span>
    
    <span class="kd">@ViewBuilder</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">activitySection</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">Section</span> <span class="p">{</span>
            <span class="k">if</span> <span class="o">!</span><span class="n">summary</span><span class="o">.</span><span class="n">metrics</span><span class="o">.</span><span class="n">workouts</span><span class="o">.</span><span class="n">isEmpty</span> <span class="p">{</span>
                <span class="kt">DividedCard</span> <span class="p">{</span>
                    <span class="kt">ForEach</span><span class="p">(</span><span class="n">summary</span><span class="o">.</span><span class="n">metrics</span><span class="o">.</span><span class="n">workouts</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="n">workout</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span> <span class="p">{</span> <span class="n">snapshot</span> <span class="k">in</span>
                        <span class="kt">NavigationLink</span> <span class="p">{</span>
                            <span class="kt">WorkoutDetailsView</span><span class="p">(</span><span class="nv">snapshot</span><span class="p">:</span> <span class="n">snapshot</span><span class="p">)</span>
                        <span class="p">}</span> <span class="nv">label</span><span class="p">:</span> <span class="p">{</span>
                            <span class="kt">WorkoutView</span><span class="p">(</span><span class="nv">snapshot</span><span class="p">:</span> <span class="n">snapshot</span><span class="p">)</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span> <span class="nv">header</span><span class="p">:</span> <span class="p">{</span>
            <span class="kt">SectionHeader</span><span class="p">(</span>
                <span class="o">.</span><span class="n">horizontal</span><span class="p">,</span>
                <span class="nv">title</span><span class="p">:</span> <span class="kt">Text</span><span class="p">(</span><span class="s">"activitySection"</span><span class="p">),</span>
                <span class="nv">systemImage</span><span class="p">:</span> <span class="s">"figure.run"</span>
            <span class="p">)</span>
            <span class="o">.</span><span class="nf">tint</span><span class="p">(</span><span class="o">.</span><span class="n">orange</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here is the code from my app showing the usage of the new UI primitives we built earlier. As you can see, we have very similar usage to the List API, but also have precise control of look and feel which allows us to reuse these primitives on screens without sections just by removing <em>SectionedSurface</em>.</p>

<p>Replacing List in SwiftUI is not about abandoning a powerful component—it’s about choosing the right tool for the job. While List remains an excellent choice for large, uniform datasets, modern SwiftUI gives us the flexibility to build something more tailored when our UI demands it.</p>

<p>By leveraging ScrollView with lazy stacks and the Container View APIs, we can recreate—and even surpass—the capabilities of <em>List</em>. Custom primitives like <em>ScrollingSurface</em>, <em>DividedCard</em>, and <em>SectionedSurface</em> demonstrate how we can compose reusable building blocks that match our product’s design language while maintaining performance and clarity. I hope you enjoy the post. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask your questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="View Composition" /><summary type="html"><![CDATA[Whenever you consider creating a scrollable screen in SwiftUI, you might think of using a List. However, it’s not always the best choice. Lists are great for displaying uniform data. For anything else, a ScrollView with a lazy stack is almost always the best option. This week, we will learn how to build a custom scrollable container in SwiftUI with precise control of look and feel.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/container.png" /><media:content medium="image" url="https://swiftwithmajid.com/public/container.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Agentic coding in Xcode</title><link href="https://swiftwithmajid.com/2026/02/10/agentic-coding-in-xcode/" rel="alternate" type="text/html" title="Agentic coding in Xcode" /><published>2026-02-10T00:00:00+00:00</published><updated>2026-02-10T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2026/02/10/agentic-coding-in-xcode</id><content type="html" xml:base="https://swiftwithmajid.com/2026/02/10/agentic-coding-in-xcode/"><![CDATA[<p>Apple has finally released Xcode 26.3, which now supports agentic coding. In this article, I’ll guide you through configuring Xcode 26.3 and utilizing the latest best practices when using agentic tools for building apps on Apple platforms.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<p>I assume you’re familiar with agentic coding tools like Codex or Claude Code. If not, they’re computer programs that allow you to share access to your codebase and engage in conversations with it to discuss your codebase, implement features, review solutions, and more.</p>

<p>Xcode 26.3 fully supports agentic coding tools like Codex, Claude Code, or any other third-party providers. To activate these tools, you need an active subscription to one of them. You can do this by navigating to <em>Xcode -&gt; Settings -&gt; Intelligence -&gt; OpenAI -&gt; Codex</em>. A similar setup is available for Claud Code.</p>

<p>Xcode not only enables the use of agentic coding tools but also provides a Model Context Protocol (MCP). The MCP allows access to Xcode features within the agentic coding tools. For instance, a coding agent can run a preview, compare it with your design requirements, or access the latest Apple documentation.</p>

<p>Xcode 26.3 comes with bundled Codex and Claude Code instances which are a bit outdated now. But no worries, you can easily replace the bundled version of them with the recent one.</p>

<p>For instance, if you’ve already installed Codex on your working machine using <em>Brew</em> (which I highly recommend), you can create a symbolic link to the directory where Xcode stores bundled versions.</p>

<blockquote>
  <p>ln -sf $(which codex) ~/Library/Developer/Xcode/CodingAssistant/Agents/Versions/26.3/codex</p>
</blockquote>

<blockquote>
  <p>ln -sf $(which claude) ~/Library/Developer/Xcode/CodingAssistant/Agents/Versions/26.3/claude</p>
</blockquote>

<p>The same approach applies to the skills folder. Skills are reusable knowledge that you can share with your agentic coding tool. For instance, it could be a document on how to cook data flow in features or some best practices on building animations, and so on. These documents may not always be necessary in your workflow, but you might need to plug them in occasionally.</p>

<blockquote>
  <p>~/Library/Developer/Xcode/CodingAssistant/codex/skills</p>
</blockquote>

<blockquote>
  <p>~/Library/Developer/Xcode/CodingAssistant/ClaudeAgentConfig/skills</p>
</blockquote>

<p>You can create symbolic links to these folders to share your skills and have a single source of them. I highly encourage you to install skills by <a href="https://github.com/AvdLee">Antoine van der Lee</a> for SwiftUI and Swift Concurrency. It might be a game changing for you.</p>

<p>Configuration files can be adjusted to seamlessly integrate custom MCPs or tailor the default models to better align with your workflow and preferences.</p>

<blockquote>
  <p>~/Library/Developer/Xcode/CodingAssistant/codex/config.toml</p>
</blockquote>

<blockquote>
  <p>~/Library/Developer/Xcode/CodingAssistant/ClaudeAgentConfig/.claude</p>
</blockquote>

<p>Agentic coding in Xcode 26.3 isn’t just a new checkbox in Settings — it’s a shift in how we build apps on Apple platforms. When you connect tools like Codex or Claude Code directly to Xcode and unlock MCP, your editor stops being just an IDE and starts becoming a collaborative environment.</p>

<p>Your agent can reason about your codebase, access documentation, run previews, and align implementation with design intent — all within the same workflow. I hope you enjoyed this one. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask any questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="Meta" /><summary type="html"><![CDATA[Apple has finally released Xcode 26.3, which now supports agentic coding. In this article, I’ll guide you through configuring Xcode 26.3 and utilizing the latest best practices when using agentic tools for building apps on Apple platforms.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/agentic-coding.jpg" /><media:content medium="image" url="https://swiftwithmajid.com/public/agentic-coding.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">On-demand resources in iOS app</title><link href="https://swiftwithmajid.com/2026/02/03/on-demand-resources-in-ios-app/" rel="alternate" type="text/html" title="On-demand resources in iOS app" /><published>2026-02-03T00:00:00+00:00</published><updated>2026-02-03T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2026/02/03/on-demand-resources-in-ios-app</id><content type="html" xml:base="https://swiftwithmajid.com/2026/02/03/on-demand-resources-in-ios-app/"><![CDATA[<p>On-Demand Resources allow you to ship a smaller initial app download and fetch additional assets like images, sounds, level data, ML models, and more only when a user requires them. This week, we’ll explore how to utilize on-demand resources to store secrets outside of the app binary.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<p>iOS handles downloading, caching, and eviction, providing a seamless streaming experience without the need for your own asset CDN logic. Most apps use on-demand resources for large blobs like level data in games or ML models. But we can also leverage the power of on-demand resources to keep secrets outside of our binary.</p>

<p>For instance, we can fetch API tokens using on-demand resources and save them in the Keychain. This makes reverse engineering our app binary more challenging.</p>

<p>First, we need to enable them in the build settings of our app target. There’s a key called “Enable On Demand Resources” that should be set to YES. Once that’s done, we can start associating app resources with tags in the Resource Tags section of app target settings. This will allow us to fetch a specific collection of resources later on by using those tags.</p>

<p>There are three types of tags: initial install tags, prefetched tags, and download-only tags. Initial install tags are downloaded from the App Store along with the app binary. Prefetched tags are downloaded as soon as the app binary is downloaded. Download-only tags are downloaded only when you request them using an API.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">OnDemandResource</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">request</span><span class="p">:</span> <span class="kt">NSBundleResourceRequest</span>

    <span class="nf">init</span><span class="p">(</span><span class="nv">tags</span><span class="p">:</span> <span class="kt">Set</span><span class="o">&lt;</span><span class="kt">String</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">request</span> <span class="o">=</span> <span class="kt">NSBundleResourceRequest</span><span class="p">(</span><span class="nv">tags</span><span class="p">:</span> <span class="n">tags</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">pin</span><span class="p">()</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">Bundle</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">isFetched</span> <span class="o">=</span> <span class="k">await</span> <span class="n">request</span><span class="o">.</span><span class="nf">conditionallyBeginAccessingResources</span><span class="p">()</span>

        <span class="k">if</span> <span class="o">!</span><span class="n">isFetched</span> <span class="p">{</span>
            <span class="k">try</span> <span class="k">await</span> <span class="n">request</span><span class="o">.</span><span class="nf">beginAccessingResources</span><span class="p">()</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="n">request</span><span class="o">.</span><span class="n">bundle</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">unpin</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">request</span><span class="o">.</span><span class="nf">endAccessingResources</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s create a type that we can use to access our on-demand resources. Here we define the <em>OnDemandResource</em> class with two functions <em>pin</em> and <em>unpin</em>. The <em>pin</em> function initiates a resource request with the provided set of tags and returns a bundle that we can use to access our resources.</p>

<p>We use the <em>conditionallyBeginAccessingResources</em> function to check if we can access resources directly. If it returns false, we download them from the App Store using <em>beginAccessingResources</em>. If downloaded, it returns true, and we get the bundle to access resources almost immediately. As soon as we finish using resource we should call <em>unpin</em> to allow system evict resources.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">resource</span> <span class="o">=</span> <span class="kt">OnDemandResource</span><span class="p">(</span><span class="nv">tags</span><span class="p">:</span> <span class="p">[</span><span class="s">"Config"</span><span class="p">])</span>
<span class="k">let</span> <span class="nv">bundle</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="n">resource</span><span class="o">.</span><span class="nf">pin</span><span class="p">()</span>
<span class="k">defer</span> <span class="p">{</span> <span class="n">resource</span><span class="o">.</span><span class="nf">unpin</span><span class="p">()</span> <span class="p">}</span>

<span class="k">if</span> <span class="k">let</span> <span class="nv">config</span> <span class="o">=</span> <span class="n">bundle</span><span class="o">.</span><span class="nf">url</span><span class="p">(</span><span class="nv">forResource</span><span class="p">:</span> <span class="s">"Config"</span><span class="p">,</span> <span class="nv">withExtension</span><span class="p">:</span> <span class="s">"json"</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// decode your config and save to Keychain</span>
<span class="p">}</span>
</code></pre></div></div>

<p>On-demand Resources are often associated with large assets, but as we’ve seen, they can also be a practical tool for improving the security posture of your iOS app. By moving sensitive data—such as API tokens—out of the main app binary and delivering them only when needed, you reduce the attack surface and make static analysis significantly harder.</p>

<p>On-demand resources can be a useful defense-in-depth technique, but they should not be treated as a security boundary on their own. On-demand resources are not encrypted by default once downloaded to the device. A determined attacker with device access can still inspect cached resources.</p>

<p>Apple mentioned that on-demand resources is a legacy technology, so migrating to Background Assets is recommended. That’s going to be the topic for the next week. I hope you enjoyed this one. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask any questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="Architecture" /><summary type="html"><![CDATA[On-Demand Resources allow you to ship a smaller initial app download and fetch additional assets like images, sounds, level data, ML models, and more only when a user requires them. This week, we’ll explore how to utilize on-demand resources to store secrets outside of the app binary.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/xcode-cloud.png" /><media:content medium="image" url="https://swiftwithmajid.com/public/xcode-cloud.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Monitoring app performance with MetricKit</title><link href="https://swiftwithmajid.com/2025/12/09/monitoring-app-performance-with-metrickit/" rel="alternate" type="text/html" title="Monitoring app performance with MetricKit" /><published>2025-12-09T00:00:00+00:00</published><updated>2025-12-09T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2025/12/09/monitoring-app-performance-with-metrickit</id><content type="html" xml:base="https://swiftwithmajid.com/2025/12/09/monitoring-app-performance-with-metrickit/"><![CDATA[<p>Xcode Organizer provides access to essential performance metrics such as crashes, energy impact, hangs, launch time, memory consumption, and app terminations. However, it lacks sufficient information to resolve certain issues, particularly app terminations. To address this, Apple introduced the MetricKit framework, enabling us to collect comprehensive diagnostics and build a detailed performance dashboard.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<p>To monitor app performance, we need to gather performance data and export it for analysis. The most straightforward way to achieve this is by using an analytics tool. Let’s begin building our app performance monitoring dashboard.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">Analytics</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">logEvent</span><span class="p">(</span><span class="n">_</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
    <span class="kd">func</span> <span class="nf">logCrash</span><span class="p">(</span><span class="n">_</span> <span class="nv">crash</span><span class="p">:</span> <span class="kt">MXCrashDiagnostic</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Next, we have to import MetricKit and set up a subscription to receive data.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">AppDelegate</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">UIApplicationDelegate</span><span class="p">,</span> <span class="kt">MXMetricManagerSubscriber</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">analytics</span><span class="p">:</span> <span class="kt">Analytics</span><span class="p">?</span>

    <span class="kd">func</span> <span class="nf">applicationDidFinishLaunching</span><span class="p">(</span><span class="n">_</span> <span class="nv">application</span><span class="p">:</span> <span class="kt">UIApplication</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">MXMetricManager</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">nonisolated</span> <span class="kd">func</span> <span class="nf">didReceive</span><span class="p">(</span><span class="n">_</span> <span class="nv">payloads</span><span class="p">:</span> <span class="p">[</span><span class="kt">MXMetricPayload</span><span class="p">])</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">payload</span> <span class="k">in</span> <span class="n">payloads</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nv">exitMetrics</span> <span class="o">=</span> <span class="n">payload</span><span class="o">.</span><span class="n">applicationExitMetrics</span><span class="p">?</span><span class="o">.</span><span class="n">backgroundExitData</span> <span class="p">{</span>
                <span class="n">analytics</span><span class="p">?</span><span class="o">.</span><span class="nf">logEvent</span><span class="p">(</span>
                    <span class="s">"performance_abnormal_exit"</span><span class="p">,</span>
                    <span class="nv">value</span><span class="p">:</span> <span class="n">exitMetrics</span><span class="o">.</span><span class="n">cumulativeAbnormalExitCount</span><span class="o">.</span><span class="nf">formatted</span><span class="p">()</span>
                <span class="p">)</span>
                
                <span class="n">analytics</span><span class="p">?</span><span class="o">.</span><span class="nf">logEvent</span><span class="p">(</span>
                    <span class="s">"performance_cpu_exit"</span><span class="p">,</span>
                    <span class="nv">value</span><span class="p">:</span> <span class="n">exitMetrics</span><span class="o">.</span><span class="n">cumulativeCPUResourceLimitExitCount</span><span class="o">.</span><span class="nf">formatted</span><span class="p">()</span>
                <span class="p">)</span>
                    
                <span class="n">analytics</span><span class="p">?</span><span class="o">.</span><span class="nf">logEvent</span><span class="p">(</span>
                    <span class="s">"performance_memory_exit"</span><span class="p">,</span>
                    <span class="nv">value</span><span class="p">:</span> <span class="n">exitMetrics</span><span class="o">.</span><span class="n">cumulativeMemoryPressureExitCount</span><span class="o">.</span><span class="nf">formatted</span><span class="p">()</span>
                <span class="p">)</span>
                
                <span class="n">analytics</span><span class="p">?</span><span class="o">.</span><span class="nf">logEvent</span><span class="p">(</span>
                    <span class="s">"performance_oom_exit"</span><span class="p">,</span>
                    <span class="nv">value</span><span class="p">:</span> <span class="n">exitMetrics</span><span class="o">.</span><span class="n">cumulativeMemoryResourceLimitExitCount</span><span class="o">.</span><span class="nf">formatted</span><span class="p">()</span>
                <span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">nonisolated</span> <span class="kd">func</span> <span class="nf">didReceive</span><span class="p">(</span><span class="n">_</span> <span class="nv">payloads</span><span class="p">:</span> <span class="p">[</span><span class="kt">MXDiagnosticPayload</span><span class="p">])</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">payload</span> <span class="k">in</span> <span class="n">payloads</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nv">crashes</span> <span class="o">=</span> <span class="n">payload</span><span class="o">.</span><span class="n">crashDiagnostics</span> <span class="p">{</span>
                <span class="k">for</span> <span class="n">crash</span> <span class="k">in</span> <span class="n">crashes</span> <span class="p">{</span>
                    <span class="n">analytics</span><span class="p">?</span><span class="o">.</span><span class="nf">logCrash</span><span class="p">(</span><span class="n">crash</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As demonstrated in the example above, we utilize the shared instance of the <em>MXMetricManager</em> type to add a subscriber. Our <em>AppDelegate</em> type conforms to the <em>MXMetricManagerSubscriber</em> protocol, which includes two optional functions that enable us to receive metrics and diagnostics.</p>

<p>The <em>MXMetricPayload</em> type contains a collection of properties that extends the <em>MXMetric</em> abstract class. For instance, it includes <em>applicationLaunchMetrics</em> and <em>applicationExitMetrics</em> properties that provide comprehensive details. In our instance, I log a few intriguing background terminations. This knowledge enables me to comprehend the reasons behind the system’s termination of the app.</p>

<p>The <em>MXDiagnosticPayload</em> type contains a collection of properties that extend the abstract <em>MXDiagnostic</em> class. For example, it includes <em>cpuExceptionDiagnostics</em> and <em>crashDiagnostics</em>. We use the <em>logCrash</em> function to extract valuable details and log them.</p>

<p>Both <em>MXMetricPayload</em> and <em>MXDiagnosticPayload</em> offer us JSON and dictionary representations of the properties, enabling us to effortlessly upload this information to our custom-made API endpoint and process it on the backend.</p>

<p>Keep in mind that the <em>MXMetricManager</em> may not always receive payloads. The system can aggregate the data and deliver it on a daily schedule. In rare cases, it may deliver more frequently, but you should not rely on any specific schedule. Both payload types provide the <em>timeStampBegin</em> and <em>timeStampEnd</em> properties, which allow us to determine the range of time covered by each payload.</p>

<p>MetricKit fills a critical gap left by Xcode Organizer by giving us deep, system-level insight into how an app behaves in real-world conditions. By subscribing to <em>MXMetricManager</em> and processing <em>MXMetricPayload</em> and <em>MXDiagnosticPayload</em>, we gain visibility into app launches, terminations, crashes, and resource usage that would otherwise be difficult—or impossible—to fully understand. I hope you enjoyed this one. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask any questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="Meta" /><summary type="html"><![CDATA[Xcode Organizer provides access to essential performance metrics such as crashes, energy impact, hangs, launch time, memory consumption, and app terminations. However, it lacks sufficient information to resolve certain issues, particularly app terminations. To address this, Apple introduced the MetricKit framework, enabling us to collect comprehensive diagnostics and build a detailed performance dashboard.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/instruments.png" /><media:content medium="image" url="https://swiftwithmajid.com/public/instruments.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Generating images in Swift using Image Playground</title><link href="https://swiftwithmajid.com/2025/11/11/generating-images-in-swift-using-image-playground/" rel="alternate" type="text/html" title="Generating images in Swift using Image Playground" /><published>2025-11-11T00:00:00+00:00</published><updated>2025-11-11T00:00:00+00:00</updated><id>https://swiftwithmajid.com/2025/11/11/generating-images-in-swift-using-image-playground</id><content type="html" xml:base="https://swiftwithmajid.com/2025/11/11/generating-images-in-swift-using-image-playground/"><![CDATA[<p>I’m continuing to work on AI-generated content in my apps, and this time, we’ll focus on image generation. You might be familiar with the Image Playground app on your Apple devices, which also has a Swift API. This week, we’ll explore how to utilize the Image Playground framework to create image content within our apps.</p>

<div class="friends">
    <span>
Your Apple Watch measures your heart rate every 4 minutes during the day. With <b>CardioBot</b>, you can easily understand the data captured by the Apple Watch so you can improve your lifestyle and discover notable patterns.
       <a href="https://apps.apple.com/us/app/cardiobot-heart-rate-on-watch/id1149412984?uo=4">Try now</a>
    </span>
</div>

<p>Image Playground framework provides us a text-to-image functionality. The core of the framework is the <em>ImageCreator</em> type. Let’s take a look at how we can use it.</p>

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

<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Eye</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{}</span>
    
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">visualize</span><span class="p">(</span><span class="nv">text</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">CGImage</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">creator</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="kt">ImageCreator</span><span class="p">()</span>

        <span class="k">let</span> <span class="nv">images</span> <span class="o">=</span> <span class="n">creator</span><span class="o">.</span><span class="nf">images</span><span class="p">(</span>
            <span class="nv">for</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="nf">text</span><span class="p">(</span><span class="n">text</span><span class="p">)],</span>
            <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">sketch</span><span class="p">,</span>
            <span class="nv">limit</span><span class="p">:</span> <span class="mi">1</span>
        <span class="p">)</span>

        <span class="k">for</span> <span class="k">try</span> <span class="k">await</span> <span class="n">image</span> <span class="k">in</span> <span class="n">images</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">image</span><span class="o">.</span><span class="n">cgImage</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="kc">nil</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As you can see in the example above, we create an instance of the <em>ImageCreator</em> type. The initializer may throw an error if the running device doesn’t support image generation.</p>

<p>Next, we create the concepts describing the image we want to generate. Here you can be creative and use a combination of text and source image if needed.</p>

<p>The final step is the call to the <em>images</em> function on an instance of the <em>ImageCreator</em> type. It requires a few parameters: <em>concepts</em>, <em>style</em> and <em>limit</em>.</p>

<p>The <em>ImageCreator</em> type supports a set of styles: <em>animation</em>, <em>illustration</em> and <em>sketch</em>. You can choose the one you need. The <em>limit</em> parameter allows you to limit the number of results, in our example I ask only for a single image. Keep in mind that system allows no more than 4 images.</p>

<p>The <em>images</em> function returns an instance of <em>AsyncSequence</em> which emits the result images one by one as soon as they become ready. That’s why we use here for loop to receive the images.</p>

<p>As I said before, we can mix and match multiple concepts to generate a single image. For example, you can provide a source image and some text.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Eye</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{}</span>
    
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">visualize</span><span class="p">(</span><span class="nv">text</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">image</span><span class="p">:</span> <span class="kt">CGImage</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">CGImage</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">creator</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="kt">ImageCreator</span><span class="p">()</span>

        <span class="k">let</span> <span class="nv">images</span> <span class="o">=</span> <span class="n">creator</span><span class="o">.</span><span class="nf">images</span><span class="p">(</span>
            <span class="nv">for</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="nf">text</span><span class="p">(</span><span class="n">text</span><span class="p">),</span> <span class="o">.</span><span class="nf">image</span><span class="p">(</span><span class="n">image</span><span class="p">)],</span>
            <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">sketch</span><span class="p">,</span>
            <span class="nv">limit</span><span class="p">:</span> <span class="mi">1</span>
        <span class="p">)</span>

        <span class="k">for</span> <span class="k">try</span> <span class="k">await</span> <span class="n">image</span> <span class="k">in</span> <span class="n">images</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">image</span><span class="o">.</span><span class="n">cgImage</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="kc">nil</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <em>ImagePlaygroundConcept</em> type provides us a few static functions allowing us to create a concept. We already use the <em>text</em> and <em>image</em> functions.</p>

<p>There are also <em>drawing</em> and <em>extracted</em> functions. The <em>drawing</em> function allows us to provide an instance of the <em>PKDrawing</em> from the PencilKit framework.</p>

<p>The <em>extracted</em> function becomes useful when you have a huge text like article and you can use it to generate the image for an article.</p>

<p>Not all the styles might be available on your device. That’s why the <em>ImageCreator</em> type provides the static property called <em>availableStyles</em>. It is an array of the supported styles. You should always check if the selected style is available and use only available one.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Eye</span> <span class="p">{</span>
    <span class="kd">public</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{}</span>
    
    <span class="kd">public</span> <span class="kd">func</span> <span class="nf">visualize</span><span class="p">(</span><span class="nv">text</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="k">async</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">CGImage</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">creator</span> <span class="o">=</span> <span class="k">try</span> <span class="k">await</span> <span class="kt">ImageCreator</span><span class="p">()</span>

        <span class="k">guard</span> <span class="k">let</span> <span class="nv">availableStyle</span> <span class="o">=</span> <span class="n">creator</span><span class="o">.</span><span class="n">availableStyles</span><span class="o">.</span><span class="n">first</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kc">nil</span>
        <span class="p">}</span>

        <span class="k">let</span> <span class="nv">style</span> <span class="o">=</span> <span class="o">!</span><span class="n">creator</span><span class="o">.</span><span class="n">availableStyles</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="o">.</span><span class="n">animation</span><span class="p">)</span> <span class="p">?</span> <span class="nv">availableStyle</span> <span class="p">:</span> <span class="o">.</span><span class="n">animation</span>

        <span class="k">let</span> <span class="nv">images</span> <span class="o">=</span> <span class="n">creator</span><span class="o">.</span><span class="nf">images</span><span class="p">(</span>
            <span class="nv">for</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="nf">text</span><span class="p">(</span><span class="n">text</span><span class="p">)],</span>
            <span class="nv">style</span><span class="p">:</span> <span class="n">style</span><span class="p">,</span>
            <span class="nv">limit</span><span class="p">:</span> <span class="mi">1</span>
        <span class="p">)</span>

        <span class="k">for</span> <span class="k">try</span> <span class="k">await</span> <span class="n">image</span> <span class="k">in</span> <span class="n">images</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">image</span><span class="o">.</span><span class="n">cgImage</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="kc">nil</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The Image Playground framework brings Apple’s generative image capabilities right into Swift, making it surprisingly simple to create visuals from text, drawings, or even existing photos. With just a few lines of code, you can generate styled images and integrate them directly into your app’s experience. I hope you enjoyed this one. Feel free to follow me on <a href="https://twitter.com/mecid">Twitter</a> and ask any questions related to this post. Thanks for reading, and see you next week!</p>]]></content><author><name>Majid Jabrayilov</name><email>cmecid@gmail.com</email></author><category term="Foundation Models" /><summary type="html"><![CDATA[I’m continuing to work on AI-generated content in my apps, and this time, we’ll focus on image generation. You might be familiar with the Image Playground app on your Apple devices, which also has a Swift API. This week, we’ll explore how to utilize the Image Playground framework to create image content within our apps.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://swiftwithmajid.com/public/fm.png" /><media:content medium="image" url="https://swiftwithmajid.com/public/fm.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>