<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>softwareanders.com</title>
	<atom:link href="https://softwareanders.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://softwareanders.com/</link>
	<description>Blog about software development - with focus on Swift and SwiftUI</description>
	<lastBuildDate>Fri, 27 Feb 2026 11:22:41 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://softwareanders.com/wp-content/uploads/2024/03/android-chrome-192x192-1-150x150.png</url>
	<title>softwareanders.com</title>
	<link>https://softwareanders.com/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>SwiftUI Skeleton Loading View: Build a Shimmer Effect from Scratch</title>
		<link>https://softwareanders.com/swiftui-skeleton-loading-view-build-a-shimmer-effect-from-scratch/</link>
					<comments>https://softwareanders.com/swiftui-skeleton-loading-view-build-a-shimmer-effect-from-scratch/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Fri, 27 Feb 2026 11:10:51 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=873</guid>

					<description><![CDATA[<p>If you&#8217;ve used apps like Twitter, Youtube or LinkedIn while they&#8217;re loading content, you&#8217;ve seen the SwiftUI skeleton screen effect — that smooth, animated highlight that sweeps across placeholder shapes. It&#8217;s one of the best ways to give users visual feedback while data loads, replacing static blank screens with a polished SwiftUI placeholder view that [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/swiftui-skeleton-loading-view-build-a-shimmer-effect-from-scratch/">SwiftUI Skeleton Loading View: Build a Shimmer Effect from Scratch</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>If you&#8217;ve used apps like Twitter, Youtube or LinkedIn while they&#8217;re loading content, you&#8217;ve seen the SwiftUI skeleton screen effect — that smooth, animated highlight that sweeps across placeholder shapes. </p>



<p>It&#8217;s one of the best ways to give users visual feedback while data loads, replacing static blank screens with a polished SwiftUI placeholder view that feels alive. </p>



<p>In this post, I&#8217;ll walk you through a quick guide on how to build it from scratch in SwiftUI.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<div class="ast-oembed-container " style="height: 100%;"><iframe title="How to Make a Shimmer Loading Effect in SwiftUI" width="1042" height="586" src="https://www.youtube.com/embed/hTHX_xnHycs?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></div>
</div></figure>



<h2 class="wp-block-heading">Project Setup</h2>



<p>Start by creating a new SwiftUI file. Right-click in your project navigator, choose <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">New File → SwiftUI View, </mark>and name it <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">ShimmerView</mark> (or whatever you prefer). This will be our reusable component.</p>



<h2 class="wp-block-heading">Step 1: Setting Up the Gray Placeholder Background</h2>



<p>The base of the shimmer effect is a gray placeholder background. Add a gray color with a low opacity to keep it easy on the eyes:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>Color.gray
    .opacity(0.2)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">gray</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">opacity</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0.2</span><span style="color: #ECEFF4">)</span></span></code></pre></div>



<p>That&#8217;s all there is to the background — simple and clean.</p>



<h2 class="wp-block-heading">Step 2: Building the LinearGradient Shimmer Effect</h2>



<p>The shimmer is a linear gradient that fades in from the left, peaks in the middle, and fades back out on the right. We place it on top of the background using the <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">.overlay</mark> modifier.</p>



<p>Because we need to know the full width of the view for the animation, we wrap everything inside a <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">GeometryReader</mark>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>.overlay(
    GeometryReader { reader in
        LinearGradient(
            colors: &#91;
                .clear,
                .gray.opacity(0.3),
                .clear
            &#93;,
            startPoint: .leading,
            endPoint: .trailing
        )
        .frame(width: 200)
    }
)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">overlay</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">GeometryReader</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> reader </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">LinearGradient</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">colors</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> &#91;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #D8DEE9">clear</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #D8DEE9">gray</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">opacity</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0.3</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #D8DEE9">clear</span></span>
<span class="line"><span style="color: #D8DEE9FF">            &#93;,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">startPoint</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">endPoint</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">trailing</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">200</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">)</span></span></code></pre></div>



<p><span style="caret-color: rgb(255, 255, 255); color: rgb(255, 255, 255); font-size: revert; white-space: normal;">The three-color gradient — clear → gray → clear — gives us that soft, fading highlight. We constrain its width to 200 points so it appears as a stripe rather than filling the whole view. Feel free to adjust this to taste.</span></p>



<h2 class="wp-block-heading">Step 3: Animating the Shimmer with KeyframeAnimator</h2>



<p>Now for the fun part: making it move. We use SwiftUI&#8217;s <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">keyframeAnimator</mark> modifier to sweep the gradient from left to right on a continuous loop.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>.keyframeAnimator(
    initialValue: 0.0,
    repeating: true
) { content, value in
    content
        .offset(x: value)
} keyframes: { _ in
    LinearKeyframe(
        reader.size.width + 200,
        duration: 1.5
    )
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">keyframeAnimator</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">initialValue</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0.0</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">repeating</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> content, value </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">    content</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">offset</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">x</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> value</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">keyframes</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> _ </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">LinearKeyframe</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        reader.size.</span><span style="color: #D8DEE9">width</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">200</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">duration</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1.5</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>A few things to note here:</p>



<ul class="wp-block-list">
<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">initialValue: 0.0</mark> — the animation starts at x = 0.</li>



<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">repeating</mark><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">: </mark></code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">true</mark> — the animation loops continuously as long as the view is on screen.</li>



<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">LinearKeyframe</mark> — this gives us a constant, even speed across the animation (no easing in or out), which is exactly the effect we want.</li>



<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">Duration of 1.5 seconds</mark> — a smooth, natural pace. Adjust to your preference.</li>
</ul>



<h2 class="wp-block-heading">Step 4: Finishing Touches</h2>



<p>You&#8217;ll notice the gradient starts in the middle of the view rather than offscreen to the left. Fix this by applying a negative x-offset to the gradient so it begins fully out of frame:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>.offset(x: -200)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">offset</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">x</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">-200</span><span style="color: #ECEFF4">)</span></span></code></pre></div>



<p>Now the shimmer sweeps all the way from left to right — exactly as expected.</p>



<h2 class="wp-block-heading">How to Apply the Shimmer to Any SwiftUI View</h2>



<p>Head over to <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">ContentView</mark> (or wherever you want to use it) and apply <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">ShimmerView</mark> as a background to any SwiftUI placeholder view using <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">.clipShape</mark>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>VStack {
    // Rectangle placeholder (e.g. for a banner or text line)
    ShimmerView()
        .clipShape(RoundedRectangle(cornerRadius: 8))
        .frame(height: 60)

    // Circle placeholder (e.g. for an avatar)
    ShimmerView()
        .clipShape(Circle())
        .frame(width: 60, height: 60)
}
.padding()</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Rectangle placeholder (e.g. for a banner or text line)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ShimmerView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">clipShape</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedRectangle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">60</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Circle placeholder (e.g. for an avatar)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ShimmerView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">clipShape</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">Circle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">60</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">60</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span></code></pre></div>



<p>By combining <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">ShimmerView</mark> with different clip shapes and frame sizes, you can mock out any kind of loading UI — profile pictures, text lines, cards, and more.</p>



<h2 class="wp-block-heading">Using the Shimmer as a SwiftUI ViewModifier</h2>



<p>The <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">ShimmerView</mark> approach works well, but there&#8217;s a more flexible and idiomatic SwiftUI way to do it — turning the shimmer into a <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">ViewModifier</mark>. This lets you apply the effect to <strong>any</strong> existing view with a single modifier call, rather than wrapping things in a dedicated component.</p>



<p>Here&#8217;s the full implementation:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>struct ShimmerModifier: ViewModifier {
    var backgroundColor: Color
    
    func body(content: Content) -> some View {
        content
            .overlay {
                GeometryReader { geometry in
                    LinearGradient(
                        colors: &#91;.clear, .gray.opacity(0.2), .clear&#93;,
                        startPoint: .leading,
                        endPoint: .trailing
                    )
                    .frame(width: 200)
                    .offset(x: -200)
                    .keyframeAnimator(initialValue: 0.0, repeating: true) { shimmerContent, value in
                        shimmerContent.offset(x: value)
                    } keyframes: { _ in
                        LinearKeyframe(geometry.size.width + 200, duration: 1.5)
                    }
                }
            }
            .background(backgroundColor)
    }
}

extension View {
    func shimmer(
        backgroundColor: Color = .gray.opacity(0.2)
    ) -> some View {
        modifier(
            ShimmerModifier(backgroundColor: backgroundColor)
        )
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ShimmerModifier</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ViewModifier </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> backgroundColor: Color</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">body</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">content</span><span style="color: #D8DEE9FF">: Content</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        content</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">overlay</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">GeometryReader</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> geometry </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">LinearGradient</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">colors</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> &#91;.</span><span style="color: #D8DEE9">clear</span><span style="color: #D8DEE9FF">, .</span><span style="color: #D8DEE9">gray</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">opacity</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0.2</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF">, .</span><span style="color: #D8DEE9">clear</span><span style="color: #D8DEE9FF">&#93;,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">startPoint</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">endPoint</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">trailing</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">200</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">offset</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">x</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">-200</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">keyframeAnimator</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">initialValue</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0.0</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">repeating</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> shimmerContent, value </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        shimmerContent.</span><span style="color: #88C0D0">offset</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">x</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> value</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">keyframes</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> _ </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">LinearKeyframe</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">geometry.size.</span><span style="color: #D8DEE9">width</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">200</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">duration</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1.5</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">backgroundColor</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">extension</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">shimmer</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">backgroundColor</span><span style="color: #D8DEE9FF">: Color </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">gray</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">opacity</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0.2</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">modifier</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">ShimmerModifier</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">backgroundColor</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> backgroundColor</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>A couple of things worth highlighting:</p>



<ul class="wp-block-list">
<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">backgroundColor</mark><strong> parameter</strong> — the background color is now part of the modifier itself, with a sensible default of <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">gray.opacity(0.2)</mark>. This means you no longer need to set the background separately on each placeholder shape.</li>



<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">extension View</mark> — the convenience extension gives you clean dot-syntax across your entire app, just like any native SwiftUI modifier.</li>
</ul>



<p>Usage is now much cleaner. Apply <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">.shimmer()</mark> directly to any shape or view:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>VStack(spacing: 16) {
    // Text line placeholder
    RoundedRectangle(cornerRadius: 8)
        .shimmer()
        .frame(height: 16)

    // Avatar placeholder
    Circle()
        .shimmer(backgroundColor: .gray.opacity(0.3))
        .frame(width: 60, height: 60)

    // Banner placeholder
    RoundedRectangle(cornerRadius: 12)
        .shimmer()
        .frame(height: 120)
}
.padding()</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">16</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Text line placeholder</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">RoundedRectangle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">shimmer</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">16</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Avatar placeholder</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">Circle</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">shimmer</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">backgroundColor</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">gray</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">opacity</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0.3</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">60</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">60</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Banner placeholder</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">RoundedRectangle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">12</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">shimmer</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">120</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span></code></pre></div>



<p>Notice how the shape itself defines the skeleton, and <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">.shimmer()</mark> just layers the animation on top. This separation of concerns makes it trivial to swap placeholder shapes or customize colors per use case.</p>



<h2 class="wp-block-heading">Summary</h2>



<p>Here&#8217;s the full picture of what we built:</p>



<ol class="wp-block-list">
<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">Gray background </mark>with low opacity as the base layer.</li>



<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">Linear gradient overlay </mark>fading clear → gray → clear, constrained to 200pt wide.</li>



<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">keyframeAnimator</mark> sweeping the gradient from left to right at constant speed.</li>



<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">Negative initial offset </mark>so the animation starts fully offscreen.</li>



<li><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">Reusable component </mark>you clip into any shape you need.</li>
</ol>



<p>It&#8217;s a small touch, but a SwiftUI skeleton screen makes a big difference in how polished your app feels. Give it a try and tweak the width, duration, and colors to match your design.</p>
<p>The post <a href="https://softwareanders.com/swiftui-skeleton-loading-view-build-a-shimmer-effect-from-scratch/">SwiftUI Skeleton Loading View: Build a Shimmer Effect from Scratch</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/swiftui-skeleton-loading-view-build-a-shimmer-effect-from-scratch/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI Firebase Authentication Tutorial: Complete iOS Login System</title>
		<link>https://softwareanders.com/swiftui-firebase-authentication-tutorial-complete-ios-login-system/</link>
					<comments>https://softwareanders.com/swiftui-firebase-authentication-tutorial-complete-ios-login-system/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Tue, 15 Jul 2025 07:28:34 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=853</guid>

					<description><![CDATA[<p>Building secure iOS authentication for your SwiftUI apps just got easier. This SwiftUI Firebase tutorial shows you how to implement a complete login system using Firebase Auth—Google&#8217;s robust authentication platform that&#8217;s been the gold standard for mobile app development. Whether you&#8217;re building your first iOS app authentication system or looking to learn more about Firebase, [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/swiftui-firebase-authentication-tutorial-complete-ios-login-system/">SwiftUI Firebase Authentication Tutorial: Complete iOS Login System</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>Building secure iOS authentication for your SwiftUI apps just got easier. This SwiftUI Firebase tutorial shows you how to implement a complete login system using Firebase Auth—Google&#8217;s robust authentication platform that&#8217;s been the gold standard for mobile app development.</p>



<p>Whether you&#8217;re building your first iOS app authentication system or looking to learn more about Firebase, this guide provides everything you need. You&#8217;ll learn to create secure sign-in, registration, and session management with clean, production-ready SwiftUI code.</p>



<p>Unlike basic authentication guides, we&#8217;ll build a complete system with proper error handling, input validation, and real-world best practices that you can deploy immediately.</p>



<h2 class="wp-block-heading">What You&#8217;ll Learn</h2>



<p>By the end of this tutorial, you&#8217;ll have a fully functional authentication system that includes:</p>



<ul class="wp-block-list">
<li>User registration with email verification</li>



<li>Secure sign-in functionality</li>



<li>Password reset capabilities</li>



<li>Session management</li>



<li>Error handling and user feedback</li>



<li>Clean, reusable SwiftUI components</li>
</ul>



<h2 class="wp-block-heading">Prerequisites</h2>



<p>Before we dive in, make sure you have:</p>



<ul class="wp-block-list">
<li>Xcode 14+ installed</li>



<li>Basic knowledge of SwiftUI</li>



<li>A Google/Firebase account (free tier available)</li>



<li>iOS 15+ as your deployment target</li>
</ul>



<h2 class="wp-block-heading">Setting Up Your Firebase Project</h2>



<p>First, let&#8217;s set up the backend infrastructure:</p>



<h3 class="wp-block-heading">1. Create a Firebase Project</h3>



<ol class="wp-block-list">
<li>Visit&nbsp;<a href="https://firebase.google.com/">firebase.google.com</a>&nbsp;and sign in with your Google account</li>



<li>Click &#8220;Create a project&#8221; and follow the setup wizard</li>



<li>Enter your project name and configure analytics (optional)</li>



<li>Wait for your project to be created (usually takes 1-2 minutes)</li>



<li>In the Firebase console, click &#8220;Add app&#8221; and select iOS</li>



<li>Register your app with your bundle identifier</li>



<li>Download the&nbsp;<code>GoogleService-Info.plist</code>&nbsp;file</li>



<li>Place the .plist file inside your project like the picture below</li>
</ol>



<figure class="wp-block-image aligncenter size-full"><img fetchpriority="high" decoding="async" width="498" height="458" src="https://softwareanders.com/wp-content/uploads/2025/07/Screenshot-2025-07-01-at-15.45.02.png" alt="" class="wp-image-855" srcset="https://softwareanders.com/wp-content/uploads/2025/07/Screenshot-2025-07-01-at-15.45.02.png 498w, https://softwareanders.com/wp-content/uploads/2025/07/Screenshot-2025-07-01-at-15.45.02-300x276.png 300w" sizes="(max-width: 498px) 100vw, 498px" /></figure>



<h3 class="wp-block-heading">2. Configure Authentication Settings</h3>



<p>To simplify this tutorial we are going to disable email verification – when you go to production, it&#8217;s good practice to keep this enabled and set it up correctly. But this tutorial is focused on implementing Firebase inside an iOS application:</p>



<ol class="wp-block-list">
<li>Go to Authentication &gt; Get Started &gt; Sign-in method</li>



<li>Click on Email/Password</li>



<li>Enable Email/Password authentication</li>



<li>Disable &#8220;Email link (passwordless sign-in)&#8221; for now</li>



<li>Save your changes</li>
</ol>



<h2 class="wp-block-heading">iOS Project Setup with Firebase</h2>



<p>Before we can do anything we need to create a new App, so open Xcode and create a new iOS project with SwiftUI interface.</p>



<h3 class="wp-block-heading">Implement Firebase</h3>



<p>Now it&#8217;s time to add Firebase to our project. Firebase provides excellent iOS SDKs that we&#8217;ll integrate using Swift Package Manager.</p>



<p>In Xcode, go to File → Add Package Dependencies and add:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>https://github.com/firebase/firebase-ios-sdk</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">https</span><span style="color: #81A1C1">:</span><span style="color: #616E88">//github.com/firebase/firebase-ios-sdk</span></span></code></pre></div>



<p>Select these packages for your project:</p>



<ul class="wp-block-list">
<li>FirebaseAuth</li>



<li>FirebaseCore</li>
</ul>



<p>Then add the&nbsp;<code>GoogleService-Info.plist</code>&nbsp;file you downloaded earlier to your Xcode project.</p>



<figure class="wp-block-image aligncenter size-full"><img fetchpriority="high" decoding="async" width="498" height="458" src="https://softwareanders.com/wp-content/uploads/2025/07/Screenshot-2025-07-01-at-15.45.02.png" alt="" class="wp-image-855" srcset="https://softwareanders.com/wp-content/uploads/2025/07/Screenshot-2025-07-01-at-15.45.02.png 498w, https://softwareanders.com/wp-content/uploads/2025/07/Screenshot-2025-07-01-at-15.45.02-300x276.png 300w" sizes="(max-width: 498px) 100vw, 498px" /></figure>



<h3 class="wp-block-heading">Setting Up Firebase in Your SwiftUI App</h3>



<p>When building SwiftUI apps that need Firebase services, you&#8217;ll need to initialize Firebase before your app starts running. We&#8217;ll do that by creating a AppDelegate class. Here&#8217;s the standard way to do it using an App Delegate:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import UIKit
import FirebaseCore

class AppDelegate: NSObject, UIApplicationDelegate {

  func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: &#91;UIApplication.LaunchOptionsKey : Any&#93;? = nil) -> Bool {
      
    FirebaseApp.configure()
      
    return true
  }
  
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> UIKit</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> FirebaseCore</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">AppDelegate</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">NSObject</span><span style="color: #D8DEE9FF">, </span><span style="color: #8FBCBB; font-weight: bold">UIApplicationDelegate </span><span style="color: #ECEFF4">{</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">application</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">application</span><span style="color: #D8DEE9FF">: UIApplication,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                   </span><span style="color: #88C0D0">didFinishLaunchingWithOptions</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">launchOptions</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">&#91;</span><span style="color: #D8DEE9FF">UIApplication.LaunchOptionsKey </span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Any</span><span style="color: #ECEFF4">&#93;</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">nil</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span></span>
<span class="line"><span style="color: #D8DEE9FF">    FirebaseApp.</span><span style="color: #88C0D0">configure</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading"><strong>What&#8217;s happening here:</strong></h3>



<ul class="wp-block-list">
<li><strong>UIKit and FirebaseCore imports</strong>&nbsp;&#8211; UIKit gives us access to the app delegate protocol, while FirebaseCore contains the initialization methods</li>



<li><strong>AppDelegate class creation</strong>&nbsp;&#8211; We create a custom app delegate that inherits from NSObject and conforms to UIApplicationDelegate</li>



<li><strong>App launch method</strong>&nbsp;&#8211; The&nbsp;<code>application(_:didFinishLaunchingWithOptions:)</code>&nbsp;method runs automatically when your app starts</li>



<li><strong>Firebase initialization</strong>&nbsp;&#8211;&nbsp;<code>FirebaseApp.configure()</code>&nbsp;reads your Firebase configuration file and sets up the connection</li>



<li><strong>Success return</strong>&nbsp;&#8211; Returning&nbsp;<code>true</code>&nbsp;tells iOS that the app launched successfully</li>
</ul>



<p>This App Delegate must be connected to your main SwiftUI App struct using&nbsp;<code>@UIApplicationDelegateAdaptor</code>&nbsp;to ensure Firebase initializes before your views load.</p>



<h2 class="wp-block-heading">Setting Up the Main App Structure for Firebase Authentication</h2>



<p>The main App struct is where everything comes together &#8211; it connects your App Delegate for Firebase initialization and launches your app&#8217;s main interface. Here&#8217;s how to structure your SwiftUI app entry point:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import SwiftUI

@main
struct FirebaseAuthTutorialApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">main</span></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> FirebaseAuthTutorialApp</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">App </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">UIApplicationDelegateAdaptor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">AppDelegate.</span><span style="color: #81A1C1">self</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> delegate</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> Scene </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">WindowGroup</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">ContentView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<h3 class="wp-block-heading"><strong>What&#8217;s happening here:</strong></h3>



<ul class="wp-block-list">
<li><strong>App entry point</strong>&nbsp;&#8211;&nbsp;<code>@main</code>&nbsp;attribute marks this as the application&#8217;s main entry point where execution begins</li>



<li><strong>App protocol conformance</strong>&nbsp;&#8211; The struct conforms to the&nbsp;<code>App</code>&nbsp;protocol, which defines the structure of your SwiftUI app</li>



<li><strong>Delegate adapter</strong>&nbsp;&#8211;&nbsp;<code>@UIApplicationDelegateAdaptor</code>&nbsp;connects your custom AppDelegate to the SwiftUI app lifecycle</li>



<li><strong>Firebase initialization</strong>&nbsp;&#8211; The AppDelegate reference ensures Firebase.configure() runs before your views load</li>



<li><strong>Scene definition</strong>&nbsp;&#8211;&nbsp;<code>WindowGroup</code>&nbsp;creates the main window that contains your app&#8217;s user interface</li>



<li><strong>Root view setup</strong>&nbsp;&#8211;&nbsp;<code>ContentView()</code>&nbsp;becomes the first view users see when the app launches</li>



<li><strong>SwiftUI lifecycle</strong>&nbsp;&#8211; This structure uses SwiftUI&#8217;s modern app lifecycle instead of the traditional UIKit approach</li>



<li><strong>Automatic management</strong>&nbsp;&#8211; SwiftUI handles window management, scene transitions, and app state changes</li>



<li><strong>Clean architecture</strong>&nbsp;&#8211; Separates app initialization (AppDelegate) from UI structure (App body)</li>
</ul>



<p>This App struct serves as the foundation that ties together Firebase initialization with your SwiftUI authentication interface, ensuring everything loads in the correct order.</p>



<h2 class="wp-block-heading">Creating a Firebase Authentication Manager for SwiftUI</h2>



<p>Managing user authentication in SwiftUI apps requires a centralized system that can track login status and communicate changes to your views. Here&#8217;s a complete AuthManager class that handles all the essential authentication functions:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import Foundation
import FirebaseAuth

class AuthManager: ObservableObject {
    @Published var isSignedIn: Bool = false
    @Published var user: User?
    
    init() {
        checkAuthStatus()
    }
    
    func checkAuthStatus() {
        if Auth.auth().currentUser != nil {
            isSignedIn = true
            user = Auth.auth().currentUser
        }else {
            isSignedIn = false
            user = nil
        }
    }
    
    @MainActor
    func createUser(email: String, password: String) async throws {
        do {
            let result = try await Auth.auth().createUser(withEmail: email, password: password)
            self.isSignedIn = true
            self.user = result.user
        } catch {
            self.isSignedIn = false
            throw error
        }
    }
    
    @MainActor
    func signIn(email: String, password: String) async throws {
        do {
            let result = try await Auth.auth().signIn(withEmail: email, password: password)
            self.isSignedIn = true
            self.user = result.user
        } catch {
            self.isSignedIn = false
            throw error
        }
    }
    
    func signOut() throws {
        do {
            try Auth.auth().signOut()
            self.isSignedIn = false
            self.user = nil
        } catch {
            throw error
        }
    }
    
    func forgotPassword(email: String) throws {
        Auth.auth().sendPasswordReset(withEmail: email)
    }
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> FirebaseAuth</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">AuthManager</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ObservableObject </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Published</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isSignedIn: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Published</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> user: User</span><span style="color: #81A1C1">?</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">checkAuthStatus</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">checkAuthStatus</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> Auth.</span><span style="color: #88C0D0">auth</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF">.currentUser </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">nil</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            isSignedIn </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">            user </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> Auth.</span><span style="color: #88C0D0">auth</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">currentUser</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            isSignedIn </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            user </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">MainActor</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">createUser</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">password</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> result </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> Auth.</span><span style="color: #88C0D0">auth</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">createUser</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">withEmail</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email, </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> result.</span><span style="color: #D8DEE9">user</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">throw</span><span style="color: #D8DEE9FF"> error</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">MainActor</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">signIn</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">password</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> result </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> Auth.</span><span style="color: #88C0D0">auth</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">signIn</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">withEmail</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email, </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> result.</span><span style="color: #D8DEE9">user</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">throw</span><span style="color: #D8DEE9FF"> error</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">signOut</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> Auth.</span><span style="color: #88C0D0">auth</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">signOut</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">throw</span><span style="color: #D8DEE9FF"> error</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">forgotPassword</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        Auth.</span><span style="color: #88C0D0">auth</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">sendPasswordReset</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">withEmail</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading"><strong>What&#8217;s happening here:</strong></h3>



<ul class="wp-block-list">
<li><strong>ObservableObject setup</strong>&nbsp;&#8211; The class publishes authentication state changes to update SwiftUI views automatically</li>



<li><strong>State tracking</strong>&nbsp;&#8211;&nbsp;<code>isSignedIn</code>&nbsp;and&nbsp;<code>user</code>&nbsp;properties maintain current authentication status</li>



<li><strong>Automatic status check</strong>&nbsp;&#8211; Constructor calls&nbsp;<code>checkAuthStatus()</code>&nbsp;to detect existing user sessions</li>



<li><strong>User registration</strong>&nbsp;&#8211;&nbsp;<code>createUser()</code>&nbsp;creates new Firebase accounts and updates local state on success</li>



<li><strong>Sign in functionality</strong>&nbsp;&#8211;&nbsp;<code>signIn()</code>&nbsp;authenticates users and stores their information locally</li>



<li><strong>Sign out capability</strong>&nbsp;&#8211;&nbsp;<code>signOut()</code>&nbsp;logs users out and clears all stored user data</li>



<li><strong>Password reset feature</strong>&nbsp;&#8211;&nbsp;<code>forgotPassword()</code>&nbsp;sends password reset emails to users who forgot their credentials</li>



<li><strong>Error propagation</strong>&nbsp;&#8211; All methods properly handle and re-throw Firebase errors for your app to manage</li>



<li><strong>Main thread safety</strong>&nbsp;&#8211; Authentication methods use&nbsp;<code>@MainActor</code>&nbsp;to ensure UI updates happen on the main thread</li>



<li><strong>Complete auth flow</strong>&nbsp;&#8211; Covers registration, login, logout, and password recovery for a full authentication system</li>
</ul>



<p>This manager provides everything needed for a complete email/password authentication system in your SwiftUI app.</p>



<h2 class="wp-block-heading">Building a Firebase Registration Screen in SwiftUI</h2>



<p>Creating a user registration form requires input validation, loading states, and error handling. Here&#8217;s a complete SwiftUI registration view that integrates with our Firebase AuthManager:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import SwiftUI

struct RegisterView: View {
    @EnvironmentObject var authManager: AuthManager
    
    @State private var email: String = ""
    @State private var password: String = ""
    @State private var confirmPassword: String = ""
    @State private var isLoading = false
    @State private var showError = false
    @State private var errorMessage: String = ""
    
    var body: some View {
        VStack(spacing: 40) {
            Text("Register to Firebase")
                .font(.title)
                .bold()
            
            VStack(alignment: .leading, spacing: 20) {
                VStack(alignment: .leading) {
                    Text("Enter email below:")
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                    TextField("Enter email", text: $email)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                }
                
                VStack(alignment: .leading) {
                    Text("Enter password below:")
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                    SecureField("Enter password", text: $password)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                    SecureField("Re-enter password", text: $confirmPassword)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                }
                
            }
            Button(action: {
                Task {
                    do {
                        isLoading = true
                        try await authManager.createUser(email: email, password: password)
                        isLoading = false
                    }catch {
                        isLoading = false
                        errorMessage = error.localizedDescription
                        print("Registration failed: \(error.localizedDescription)")
                        showError = true
                    }
                }
            }) {
                HStack {
                    if isLoading {
                        ProgressView()
                            .progressViewStyle(CircularProgressViewStyle(tint: .white))
                            .scaleEffect(0.8)
                    } else {
                        Text("Signup using Firebase")
                    }
                }
                .font(.headline)
                .foregroundColor(.white)
                .padding()
                .background(Color.blue)
                .cornerRadius(8)
            }
        }
        .alert("Registration failed!", isPresented: $showError) {
            Button("OK") {
                showError = false
            }
        } message: {
            Text("\(errorMessage)")
        }
        .padding()
    }
}

#Preview {
    RegisterView()
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> RegisterView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">EnvironmentObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager: AuthManager</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> email: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> password: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> confirmPassword: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> showError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> errorMessage: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">40</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Register to Firebase</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">20</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter email below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter email</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter password below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">SecureField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">SecureField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Re-enter password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $confirmPassword</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">action</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> authManager.</span><span style="color: #88C0D0">createUser</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email, </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        errorMessage </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> error.</span><span style="color: #D8DEE9">localizedDescription</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Registration failed: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error.</span><span style="color: #D8DEE9">localizedDescription</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        showError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">HStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> isLoading </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">ProgressView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">progressViewStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">CircularProgressViewStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">tint</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">scaleEffect</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0.8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Signup using Firebase</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">headline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">blue</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">alert</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Registration failed!</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isPresented</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $showError</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">OK</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                showError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">errorMessage</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">RegisterView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading"><strong>What&#8217;s happening here:</strong></h3>



<ul class="wp-block-list">
<li><strong>Environment object connection</strong>&nbsp;&#8211;&nbsp;<code>@EnvironmentObject</code>&nbsp;connects the view to the shared AuthManager instance</li>



<li><strong>State management</strong>&nbsp;&#8211;&nbsp;<code>@State</code>&nbsp;properties track user input, loading status, and error states</li>



<li><strong>Form structure</strong>&nbsp;&#8211; VStack layout creates a clean registration form with proper spacing</li>



<li><strong>Input fields</strong>&nbsp;&#8211; TextField for email and SecureField for password entries with rounded border styling</li>



<li><strong>Password confirmation</strong>&nbsp;&#8211; Second SecureField allows users to verify their password input</li>



<li><strong>Async registration</strong>&nbsp;&#8211; Button action uses Task to handle the async&nbsp;<code>createUser()</code>&nbsp;method</li>



<li><strong>Loading indicator</strong>&nbsp;&#8211; ProgressView appears during registration process, replacing button text</li>



<li><strong>Error handling</strong>&nbsp;&#8211; Catches registration failures and displays user-friendly error messages</li>



<li><strong>Alert system</strong>&nbsp;&#8211; Shows error alerts with specific failure messages from Firebase</li>



<li><strong>Visual feedback</strong>&nbsp;&#8211; Button changes appearance during loading and shows appropriate content</li>



<li><strong>Clean UI design</strong>&nbsp;&#8211; Consistent styling with proper typography, colors, and spacing throughout</li>
</ul>



<p>This registration view provides a complete user experience with proper validation, feedback, and error handling for Firebase authentication.</p>



<h2 class="wp-block-heading">Creating a Complete Firebase Sign-In Screen with Navigation</h2>



<p>This SwiftUI view provides a full authentication interface with sign-in functionality and navigation to registration and password reset screens. Here&#8217;s the complete implementation:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import SwiftUI

struct SignInView: View {
    @EnvironmentObject var authManager: AuthManager
    @State private var email: String = ""
    @State private var password: String = ""
    @State private var showError: Bool = false
    @State private var errorMessage: String = ""
    
    var body: some View {
        NavigationStack {
            
            
            VStack(spacing: 40) {
                Text("Firebase AUTH tutorial")
                    .font(.title)
                    .bold()
                
                
                VStack(alignment: .leading, spacing: 20) {
                    VStack(alignment: .leading, spacing: 3) {
                        Text("Enter your email below:")
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                        TextField("Enter your email", text: $email)
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                    }
                    
                    VStack(alignment: .leading, spacing: 3) {
                        Text("Enter your password below:")
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                        SecureField("Enter your password", text: $password)
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                    }
                    
                }
                
                VStackLayout(spacing: 10) {
                    Button(action: {
                        Task {
                            do {
                                try await authManager.signIn(email: email, password: password)
                            } catch {
                                print("Login failed: \(error.localizedDescription)")
                                errorMessage = error.localizedDescription
                                showError = true
                            }
                        }
                    }) {
                        Text("Login using Firebase")
                            .font(.headline)
                            .foregroundColor(.white)
                            .padding()
                            .background(Color.blue)
                            .cornerRadius(8)
                    }
                    
                    NavigationLink(destination:
                                    RegisterView()
                        .environmentObject(authManager)
                    ) {
                        Text("Register")
                            .font(.subheadline)
                    }
                    
                    NavigationLink(destination:
                                    ForgotPasswordView()
                        .environmentObject(authManager)
                    ) {
                        Text("Forgot password")
                            .font(.subheadline)
                    }
                    
                }
            }
            .alert("Login failed!", isPresented: $showError) {
                Button("OK") {
                    showError = false
                }
            } message: {
                Text("\(errorMessage)")
            }
            .padding()
        }
    }
}

#Preview {
    SignInView()
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> SignInView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">EnvironmentObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager: AuthManager</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> email: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> password: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> showError: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> errorMessage: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">NavigationStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">40</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Firebase AUTH tutorial</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">20</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your email below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your email</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your password below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">SecureField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStackLayout</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">10</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">action</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> authManager.</span><span style="color: #88C0D0">signIn</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email, </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Login failed: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error.</span><span style="color: #D8DEE9">localizedDescription</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                errorMessage </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> error.</span><span style="color: #D8DEE9">localizedDescription</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                showError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Login using Firebase</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">headline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">blue</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">NavigationLink</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">destination</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                    </span><span style="color: #88C0D0">RegisterView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">environmentObject</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">authManager</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Register</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">NavigationLink</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">destination</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                    </span><span style="color: #88C0D0">ForgotPasswordView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">environmentObject</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">authManager</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Forgot password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">alert</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Login failed!</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isPresented</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $showError</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">OK</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    showError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">errorMessage</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">SignInView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading"><strong>What&#8217;s happening here:</strong></h3>



<ul class="wp-block-list">
<li><strong>Navigation wrapper</strong>&nbsp;&#8211;&nbsp;<code>NavigationStack</code>&nbsp;enables navigation between sign-in, registration, and password reset screens</li>



<li><strong>AuthManager integration</strong>&nbsp;&#8211;&nbsp;<code>@EnvironmentObject</code>&nbsp;connects to the shared authentication manager</li>



<li><strong>Input state management</strong>&nbsp;&#8211;&nbsp;<code>@State</code>&nbsp;properties track email, password, and error states</li>



<li><strong>Clean form layout</strong>&nbsp;&#8211; VStack structure creates organized input sections with proper spacing</li>



<li><strong>Input fields</strong>&nbsp;&#8211; TextField for email and SecureField for password with rounded border styling</li>



<li><strong>Async sign-in</strong>&nbsp;&#8211; Button uses Task to handle the async&nbsp;<code>signIn()</code>&nbsp;method from AuthManager</li>



<li><strong>Error handling</strong>&nbsp;&#8211; Catches sign-in failures and displays specific error messages to users</li>



<li><strong>Navigation links</strong>&nbsp;&#8211; NavigationLink components provide seamless transitions to other authentication screens</li>



<li><strong>Environment object passing</strong>&nbsp;&#8211; Each destination view receives the AuthManager through&nbsp;<code>.environmentObject()</code></li>



<li><strong>User-friendly navigation</strong>&nbsp;&#8211; Register and forgot password links offer alternative authentication paths</li>



<li><strong>Alert system</strong>&nbsp;&#8211; Error alerts show Firebase authentication failures with helpful messages</li>



<li><strong>Consistent styling</strong>&nbsp;&#8211; Uniform typography, colors, and spacing throughout the interface</li>
</ul>



<p>This sign-in view serves as the main authentication hub, connecting users to all essential authentication functions in your Firebase-powered SwiftUI app.</p>



<h2 class="wp-block-heading">Forgot password screen for Firebase authentication</h2>



<p>When users forget their passwords, they need a simple way to reset them. Here&#8217;s a complete SwiftUI view that handles password reset requests through Firebase:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import SwiftUI

struct ForgotPasswordView: View {
    @EnvironmentObject var authManager: AuthManager
    @State private var email: String = ""
    @State private var showSuccess = false
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        NavigationStack {
            VStack(spacing: 40) {
                Text("Forgot Password")
                    .font(.title)
                    .bold()
                
                VStack(alignment: .leading, spacing: 20) {
                    VStack(alignment: .leading, spacing: 3) {
                        Text("Enter your email below:")
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                        TextField("Enter your email", text: $email)
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                            .keyboardType(.emailAddress)
                            .autocapitalization(.none)
                    }
                }
                
                VStackLayout(spacing: 10) {
                    Button(action: {
                        Task {
                            authManager.forgotPassword(email: email)
                            showSuccess = true
                        }
                    }) {
                        HStack {
                            Text("Send Reset Link")
                        }
                        .font(.headline)
                        .foregroundColor(.white)
                        .padding()
                        .background(Color.blue)
                        .cornerRadius(8)
                    }
                    .disabled(email.isEmpty)
                    
                    Button("Back to Sign In") {
                        dismiss()
                    }
                    .font(.subheadline)
                }
            }
            .padding()
            .alert("Reset Link Sent!", isPresented: $showSuccess) {
                Button("OK") {
                    dismiss()
                }
            } message: {
                Text("We've sent a password reset link to \(email). Please check your inbox.")
            }
        }
    }
}

#Preview {
    ForgotPasswordView()
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ForgotPasswordView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">EnvironmentObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager: AuthManager</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> email: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> showSuccess </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Environment</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">\.</span><span style="color: #D8DEE9">dismiss</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> dismiss</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">NavigationStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">40</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Forgot Password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">20</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your email below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your email</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">keyboardType</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">emailAddress</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">autocapitalization</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.none</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStackLayout</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">10</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">action</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            authManager.</span><span style="color: #88C0D0">forgotPassword</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            showSuccess </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">HStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Send Reset Link</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">headline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">blue</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">disabled</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">email.isEmpty</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Back to Sign In</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">dismiss</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">alert</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Reset Link Sent!</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isPresented</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $showSuccess</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">OK</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">dismiss</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">We&#39;ve sent a password reset link to </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">email</span><span style="color: #81A1C1">)</span><span style="color: #A3BE8C">. Please check your inbox.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ForgotPasswordView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading"><strong>What&#8217;s happening here:</strong></h3>



<ul class="wp-block-list">
<li><strong>Environment integration</strong>&nbsp;&#8211;&nbsp;<code>@EnvironmentObject</code>&nbsp;connects to the shared AuthManager and&nbsp;<code>@Environment(\.dismiss)</code>&nbsp;handles view dismissal</li>



<li><strong>Simple state management</strong>&nbsp;&#8211;&nbsp;<code>@State</code>&nbsp;properties track email input and success alert visibility</li>



<li><strong>Clean interface design</strong>&nbsp;&#8211; VStack layout creates a focused, single-purpose screen with proper spacing</li>



<li><strong>Email-optimized input</strong>&nbsp;&#8211; TextField configured with email keyboard type and disabled autocapitalization</li>



<li><strong>Smart button states</strong>&nbsp;&#8211; Send button is disabled when email field is empty to prevent invalid requests</li>



<li><strong>Password reset functionality</strong>&nbsp;&#8211; Calls&nbsp;<code>authManager.forgotPassword()</code>&nbsp;to send reset email through Firebase</li>



<li><strong>Success feedback</strong>&nbsp;&#8211; Alert confirms when reset link has been sent with personalized message</li>



<li><strong>Navigation handling</strong>&nbsp;&#8211; Back button and success alert both dismiss the view to return to sign-in</li>



<li><strong>User experience flow</strong>&nbsp;&#8211; Task wrapper handles the password reset request smoothly</li>



<li><strong>Accessibility features</strong>&nbsp;&#8211; Proper keyboard type and input formatting for better usability</li>



<li><strong>Clean visual hierarchy</strong>&nbsp;&#8211; Consistent styling matches other authentication screens in the app</li>
</ul>



<p>This password reset view provides a streamlined experience for users who need to recover their accounts, integrating seamlessly with your Firebase authentication system.</p>



<h2 class="wp-block-heading">Creating the Main Content View with Authentication Flow</h2>



<p>The ContentView acts as your app&#8217;s traffic controller, deciding whether to show the authentication screens or the main app content based on user login status. Here&#8217;s how to implement this crucial navigation logic:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import SwiftUI

struct ContentView: View {
    @StateObject private var authManager = AuthManager()

    var body: some View {
        if authManager.isSignedIn {
            HomeView()
                .environmentObject(authManager)
        }else {
            SignInView()
                .environmentObject(authManager)
        }
    }
}

#Preview {
    ContentView()
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ContentView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">StateObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">AuthManager</span><span style="color: #ECEFF4">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> authManager.isSignedIn </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">HomeView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">environmentObject</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">authManager</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">SignInView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">environmentObject</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">authManager</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ContentView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading"><strong>What&#8217;s happening here:</strong></h3>



<ul class="wp-block-list">
<li><strong>Creates AuthManager</strong>&nbsp;&#8211;&nbsp;<code>@StateObject</code>&nbsp;makes a single AuthManager for the whole app</li>



<li><strong>Checks login status</strong>&nbsp;&#8211; Shows HomeView if signed in, SignInView if not</li>



<li><strong>Shares AuthManager</strong>&nbsp;&#8211;&nbsp;<code>.environmentObject()</code>&nbsp;lets other views use the same AuthManager</li>



<li><strong>Automatic updates</strong>&nbsp;&#8211; View changes when user signs in or out</li>
</ul>



<p>This is your app&#8217;s main decision point &#8211; it decides what screen to show based on whether the user is logged in.</p>



<h2 class="wp-block-heading">Basic Home Screen with Sign Out</h2>



<p>This HomeView shows what authenticated users see, with a simple sign-out button:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import SwiftUI

struct HomeView: View {
    @EnvironmentObject var authManager: AuthManager

    var body: some View {
        Text("Hello, World!")
        
        Button {
            Task {
                 try authManager.signOut()
            }
        
        }label: {
            Text("Sign Out")
        }
    }
}

#Preview {
    HomeView()
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> HomeView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">EnvironmentObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager: AuthManager</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Hello, World!</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Button</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                 </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> authManager.</span><span style="color: #88C0D0">signOut</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Sign Out</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">HomeView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading"><strong>What&#8217;s happening here:</strong></h3>



<ul class="wp-block-list">
<li><strong>Welcome text</strong>&nbsp;&#8211; Shows &#8220;Hello, World!&#8221; to logged-in users</li>



<li><strong>Sign out button</strong>&nbsp;&#8211; Logs the user out when tapped</li>



<li><strong>Auto navigation</strong>&nbsp;&#8211; App goes back to sign-in screen after sign out</li>
</ul>



<p>Simple home screen that lets users sign out and return to the login flow.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p>You&#8217;ve created a professional-grade authentication system that would typically take weeks to build from scratch. Your SwiftUI app now includes secure registration, reliable sign-in, password recovery, and persistent sessions—all leveraging Firebase&#8217;s battle-tested security infrastructure. </p>



<p>Now you just need to build the rest of your application <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> </p>



<h2 class="wp-block-heading"><a href="#get-the-complete-code" id="get-the-complete-code"></a>Get the Complete Code</h2>



<p><strong><a href="https://github.com/AndersSoftware/Firebase-Auth/">Download the full source code on GitHub →</a></strong></p>



<h2 class="wp-block-heading">Frequently Asked Questions &#8211; SwiftUI Firebase Authentication</h2>



<h3 class="wp-block-heading">Auth domain not authorized&#8221; error &#8211; what does this mean?</h3>



<p>In Firebase Console, go to Authentication &gt; Settings &gt; Authorized domains and add your development/production domains.</p>



<h3 class="wp-block-heading">Can I customize the password reset email template?</h3>



<p>Yes, in Firebase Console go to Authentication &gt; Templates to customize email templates, including branding and content.</p>



<h3 class="wp-block-heading">Do I need a paid Firebase account to use authentication?</h3>



<p>No, Firebase Authentication is free for up to 10,000 monthly active users. The free tier includes email/password, phone, and social logins.</p>



<h3 class="wp-block-heading">Why am I getting &#8220;GoogleService-Info.plist not found&#8221; error?</h3>



<p>Ensure the .plist file is added to your Xcode project (not just the folder) and is included in your app target. Check that the bundle identifier matches your Firebase project.</p>



<h3 class="wp-block-heading">How do I handle authentication state across app launches?</h3>



<p>Firebase automatically maintains authentication state. The&nbsp;<code>checkAuthStatus()</code>&nbsp;method in AuthManager detects existing sessions when the app starts.</p>



<h3 class="wp-block-heading">How do I implement proper password validation?</h3>



<p>Add validation in your registration view: minimum 8 characters, mix of letters/numbers/symbols. Firebase also provides built-in password strength requirements.</p>



<h3 class="wp-block-heading">Can I add social login (Google, Apple) to this setup?</h3>



<p>Yes, you can extend the AuthManager to include social providers. You&#8217;ll need to add the respective Firebase provider dependencies and configure them in your Firebase console.</p>
<p>The post <a href="https://softwareanders.com/swiftui-firebase-authentication-tutorial-complete-ios-login-system/">SwiftUI Firebase Authentication Tutorial: Complete iOS Login System</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/swiftui-firebase-authentication-tutorial-complete-ios-login-system/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Alamofire Swift Tutorial: Build Robust iOS Networking with Less Code</title>
		<link>https://softwareanders.com/alamofire-swift-tutorial-build-robust-ios-networking-with-less-code/</link>
					<comments>https://softwareanders.com/alamofire-swift-tutorial-build-robust-ios-networking-with-less-code/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Mon, 23 Jun 2025 19:55:34 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=836</guid>

					<description><![CDATA[<p>If you&#8217;re an iOS developer looking to implement robust networking in your Swift applications, you&#8217;ve likely encountered the debate between using Apple&#8217;s native URLSession and the popular third-party&#160;Swift networking library, Alamofire. In this&#160;Alamofire tutorial, we&#8217;ll explore why Alamofire continues to dominate iOS networking in 2025 and how you can create a soild APIManager and use&#160;Alamofire [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/alamofire-swift-tutorial-build-robust-ios-networking-with-less-code/">Alamofire Swift Tutorial: Build Robust iOS Networking with Less Code</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>If you&#8217;re an iOS developer looking to implement robust networking in your Swift applications, you&#8217;ve likely encountered the debate between using Apple&#8217;s native URLSession and the popular third-party&nbsp;<strong>Swift networking library</strong>, Alamofire. In this&nbsp;<strong>Alamofire tutorial</strong>, we&#8217;ll explore why Alamofire continues to dominate iOS networking in 2025 and how you can create a soild APIManager and use&nbsp;<strong>Alamofire best practices iOS</strong>&nbsp;to build better apps.</p>



<p>Whether you&#8217;re just starting your&nbsp;<strong>Alamofire iOS</strong>&nbsp;journey or looking to refine your existing implementation, this guide will provide you with the essential knowledge to make informed decisions about&nbsp;<strong>how to use Alamofire in Swift 2025</strong>&nbsp;effectively.</p>



<p>The networking layer is often the backbone of iOS applications, connecting your users to the data and services they need. With the increasing complexity of APIs, authentication requirements, and performance expectations in 2025, choosing the right networking approach has never been more critical for your app&#8217;s success.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" width="800" height="600" src="https://softwareanders.com/wp-content/uploads/2025/06/alamofire-api-flow-3.png" alt="" class="wp-image-846" style="width:744px;height:auto" srcset="https://softwareanders.com/wp-content/uploads/2025/06/alamofire-api-flow-3.png 800w, https://softwareanders.com/wp-content/uploads/2025/06/alamofire-api-flow-3-300x225.png 300w, https://softwareanders.com/wp-content/uploads/2025/06/alamofire-api-flow-3-768x576.png 768w" sizes="(max-width: 800px) 100vw, 800px" /></figure>



<h2 class="wp-block-heading">Why Alamofire Remains the Top Choice for Swift Networking in 2025</h2>



<p>In the rapidly evolving world of iOS development,&nbsp;<strong>Alamofire</strong>&nbsp;has maintained its position as the premier&nbsp;<strong>Swift networking library</strong>&nbsp;for compelling reasons that become even more relevant in 2025&#8217;s development landscape.</p>



<h3 class="wp-block-heading">Developer Productivity and Code Quality</h3>



<p><strong>Alamofire best practices iOS</strong>&nbsp;consistently demonstrate that developers can achieve complex networking functionality with significantly less code compared to URLSession implementations. While URLSession requires extensive boilerplate for common tasks like JSON parsing, authentication, and error handling, Alamofire provides these features through an elegant, chainable API.</p>



<p>The time savings are substantial. What might take 50-100 lines of URLSession code can often be accomplished in 10-15 lines with Alamofire, without sacrificing functionality or maintainability. This efficiency translates directly to faster development cycles and reduced maintenance overhead.</p>



<h3 class="wp-block-heading">Maintenance and Reliability</h3>



<p>Unlike homegrown networking solutions that require ongoing maintenance and testing, Alamofire provides proven reliability:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th><strong>Reliability Factor</strong></th><th><strong>Description</strong></th><th><strong>Impact</strong></th></tr><tr><td>Battle-Tested</td><td>Used by thousands of production apps with millions of users</td><td>Proven stability in real-world conditions</td></tr><tr><td>Actively Maintained</td><td>Regular updates keeping pace with iOS evolution</td><td>Always compatible with latest iOS features</td></tr><tr><td>Well-Tested</td><td>Comprehensive test suite across iOS versions</td><td>Consistent behavior across different devices</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Setting Up Secure Token Storage</h2>



<p>Before building our API manager, we need a way to securely store authentication tokens. iOS Keychain is perfect for this &#8211; it&#8217;s encrypted and persists across app updates and device restarts.</p>



<h3 class="wp-block-heading">The KeychainHelper Foundation</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Foundation

final class KeychainHelper {
    static let standard = KeychainHelper()
    private init() {}
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">final</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">KeychainHelper</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> standard </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">KeychainHelper</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>We&#8217;re using the singleton pattern &#8211; one instance handles all keychain operations throughout our app. The private initializer ensures no one can create additional instances.</p>



<h2 class="wp-block-heading">Saving Data to Keychain</h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func save(_ data: Data, service: String, account: String) {
    let query = [
        kSecValueData: data,
        kSecClass: kSecClassGenericPassword,
        kSecAttrService: service,
        kSecAttrAccount: account,
    ] as CFDictionary
    
    let status = SecItemAdd(query, nil)
    
    if status != errSecSuccess {
        print(&#8220;Error: \(status)&#8221;)
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">save</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">: Data, </span><span style="color: #88C0D0">service</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">account</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> query </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecValueData</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> data,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecClass</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> kSecClassGenericPassword,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecAttrService</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> service,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecAttrAccount</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> account,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ] </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> CFDictionary</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> status </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">SecItemAdd</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">query, </span><span style="color: #81A1C1">nil</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> status </span><span style="color: #81A1C1">!=</span><span style="color: #D8DEE9FF"> errSecSuccess </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Error: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">status</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The&nbsp;<code>save</code>&nbsp;method creates a dictionary with keychain attributes. The&nbsp;<code>service</code>&nbsp;and&nbsp;<code>account</code>&nbsp;parameters act like a compound key &#8211; think of&nbsp;<code>service</code>&nbsp;as your app identifier and&nbsp;<code>account</code>&nbsp;as the specific data type (like &#8220;accesstoken&#8221;).</p>



<h3 class="wp-block-heading">Reading Data Back</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func read(service: String, account: String) -> Data? {
    let query = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrService: service,
        kSecAttrAccount: account,
        kSecReturnData: true
    ] as CFDictionary
        
    var result: AnyObject?
    SecItemCopyMatching(query, &amp;result)
    return result as? Data
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">read</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">service</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">account</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> Data</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> query </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecClass</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> kSecClassGenericPassword,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecAttrService</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> service,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecAttrAccount</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> account,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecReturnData</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ] </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> CFDictionary</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> result: </span><span style="color: #8FBCBB">AnyObject</span><span style="color: #81A1C1">?</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">SecItemCopyMatching</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">query, </span><span style="color: #81A1C1">&amp;</span><span style="color: #D8DEE9FF">result</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> result </span><span style="color: #81A1C1">as?</span><span style="color: #D8DEE9FF"> Data</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>Notice&nbsp;<code>kSecReturnData: true</code>&nbsp;&#8211; this tells the keychain to return the actual data, not just metadata. The&nbsp;<code>&amp;result</code>parameter is where the keychain stores the found data.</p>



<h3 class="wp-block-heading">Updating Existing Data</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func update(_ data: Data, service: String, account: String) {
    let query = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrService: service,
        kSecAttrAccount: account
    ] as CFDictionary
        
    let updatedData = [kSecValueData: data] as CFDictionary
    SecItemUpdate(query, updatedData)
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">update</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF">: Data, </span><span style="color: #88C0D0">service</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">account</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> query </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecClass</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> kSecClassGenericPassword,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecAttrService</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> service,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecAttrAccount</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> account</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ] </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> CFDictionary</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> updatedData </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [kSecValueData</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> data] </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> CFDictionary</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">SecItemUpdate</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">query, updatedData</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>When you need to update a token (like after refresh), use&nbsp;<code>SecItemUpdate</code>&nbsp;instead of deleting and re-adding.</p>



<h3 class="wp-block-heading">Cleanup with Delete</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func delete(service: String, account: String) {
    let query = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrService: service,
        kSecAttrAccount: account
    ] as CFDictionary
        
    SecItemDelete(query)
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">delete</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">service</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">account</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> query </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecClass</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> kSecClassGenericPassword,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecAttrService</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> service,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        kSecAttrAccount</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> account</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ] </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> CFDictionary</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">SecItemDelete</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">query</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>Perfect for logout scenarios or when tokens expire.</p>



<h3 class="wp-block-heading">Why This Approach Works</h3>



<ul class="wp-block-list">
<li><strong>Secure</strong>: Data is encrypted by the system</li>



<li><strong>Persistent</strong>: Survives app updates and device restarts</li>



<li><strong>Simple</strong>: Clean API that hides keychain complexity</li>



<li><strong>Flexible</strong>: Service/account pattern lets you store multiple types of credentials</li>
</ul>



<p>Now that we have secure token storage, let&#8217;s build an API manager that uses it automatically</p>



<h2 class="wp-block-heading">Alamofire in Action: Essential Code Examples for Swift 2025</h2>



<p>When building iOS apps that rely on network requests, you need an API manager that handles authentication, retries, and errors gracefully. Let&#8217;s build one step by step.</p>



<h3 class="wp-block-heading">Starting with the Foundation</h3>



<p>First, let&#8217;s create our basic API manager structure:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Alamofire

public class APIManager {
    public static let shared = APIManager()
    
    func execute(serverUrl: String, httpBody: Data?, method: HTTPMethod = .get, parameters: Parameters? = nil, success: @escaping ((AFDataResponse&lt;String>) -> Void), failure: @escaping ((APIError) -> Void)) {
        // Implementation coming next&#8230;
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Alamofire</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">APIManager</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> shared </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">APIManager</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">serverUrl</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">httpBody</span><span style="color: #D8DEE9FF">: Data</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">method</span><span style="color: #D8DEE9FF">: HTTPMethod </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">get</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">parameters</span><span style="color: #D8DEE9FF">: Parameters</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">nil</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">success</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">escaping</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">((</span><span style="color: #D8DEE9FF">AFDataResponse</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Void</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">failure</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">escaping</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">((</span><span style="color: #D8DEE9FF">APIError</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Void</span><span style="color: #ECEFF4">))</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Implementation coming next...</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The singleton pattern gives us a single point of access throughout our app. The&nbsp;<code>execute</code>&nbsp;method is our main workhorse &#8211; notice how it accepts flexible parameters and uses completion handlers for async responses.</p>



<h3 class="wp-block-heading">Adding Request Setup and Validation</h3>



<p>Now let&#8217;s implement the core request logic:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func execute(serverUrl: String, httpBody: Data?, method: HTTPMethod = .get, parameters: Parameters? = nil, success: @escaping ((AFDataResponse&lt;String>) -> Void), failure: @escaping ((APIError) -> Void)) {
    guard let url = URL(string: serverUrl) else {
        failure(APIError.invalidURL)
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = method.rawValue
    request.setValue(&#8220;application/json&#8221;, forHTTPHeaderField: &#8220;Content-Type&#8221;)
    request.httpBody = httpBody
    request.timeoutInterval = 30
    
    AF.request(request, interceptor: Interceptor()).validate().responseString { response in
        switch response.result {
        case .success:
            success(response)
        case .failure(let error):
            let apiError = APIError.httpError(
                statusCode: response.response?.statusCode ?? 0,
                message: error.localizedDescription
            )
            failure(apiError)
        }
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">serverUrl</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">httpBody</span><span style="color: #D8DEE9FF">: Data</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">method</span><span style="color: #D8DEE9FF">: HTTPMethod </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">get</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">parameters</span><span style="color: #D8DEE9FF">: Parameters</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">nil</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">success</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">escaping</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">((</span><span style="color: #D8DEE9FF">AFDataResponse</span><span style="color: #ECEFF4">&lt;</span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Void</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">failure</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">escaping</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">((</span><span style="color: #D8DEE9FF">APIError</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Void</span><span style="color: #ECEFF4">))</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">guard</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> url </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">URL</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">string</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> serverUrl</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">failure</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">APIError.</span><span style="color: #D8DEE9">invalidURL</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> request </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">URLRequest</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">url</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> url</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    request.</span><span style="color: #D8DEE9">httpMethod</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> method.rawValue</span></span>
<span class="line"><span style="color: #D8DEE9FF">    request.</span><span style="color: #88C0D0">setValue</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">application/json</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">forHTTPHeaderField</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Content-Type</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    request.</span><span style="color: #D8DEE9">httpBody</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> httpBody</span></span>
<span class="line"><span style="color: #D8DEE9FF">    request.</span><span style="color: #D8DEE9">timeoutInterval</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">30</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    AF.</span><span style="color: #88C0D0">request</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">request, </span><span style="color: #88C0D0">interceptor</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Interceptor</span><span style="color: #ECEFF4">())</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">validate</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">responseString</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> response </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">switch</span><span style="color: #D8DEE9FF"> response.result </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">success</span><span style="color: #81A1C1">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">success</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">response</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> .</span><span style="color: #88C0D0">failure</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> error</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> apiError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> APIError.</span><span style="color: #88C0D0">httpError</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">statusCode</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> response.</span><span style="color: #D8DEE9">response</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">statusCode</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">??</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> error.</span><span style="color: #D8DEE9">localizedDescription</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">failure</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">apiError</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>Key points: URL validation happens first, we set standard JSON headers, and notice the&nbsp;<code>interceptor: Interceptor()</code>parameter &#8211; that&#8217;s where the magic happens.</p>



<h3 class="wp-block-heading">Automatic Authentication with Interceptors</h3>



<p>Alamofire&#8217;s&nbsp;<code>RequestInterceptor</code>&nbsp;protocol lets us modify requests automatically:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>final class Interceptor: RequestInterceptor {
    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result&lt;URLRequest, Error>) -> Void) {
        var urlRequestToUse = urlRequest
        
        if let accessToken = KeychainHelper.standard.read(service: &#8220;accesstoken&#8221;, account: &#8220;madplan&#8221;),
           let tokenString = String(data: accessToken, encoding: .utf8) {
            let cleanToken = tokenString.replacingOccurrences(of: &#8220;\&#8221;&#8221;, with: &#8220;&#8221;)
            urlRequestToUse.setValue(&#8220;Bearer \(cleanToken)&#8221;, forHTTPHeaderField: &#8220;Authorization&#8221;)
        }
        
        completion(.success(urlRequestToUse))
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">final</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Interceptor</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">RequestInterceptor </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">adapt</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">urlRequest</span><span style="color: #D8DEE9FF">: URLRequest, </span><span style="color: #88C0D0">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #D8DEE9FF">: Session, </span><span style="color: #88C0D0">completion</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">escaping</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Result</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">URLRequest, </span><span style="color: #8FBCBB">Error</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Void</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> urlRequestToUse </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> urlRequest</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> accessToken </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> KeychainHelper.standard.</span><span style="color: #88C0D0">read</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">service</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">accesstoken</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">account</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">madplan</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">           </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> tokenString </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">data</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> accessToken, </span><span style="color: #88C0D0">encoding</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .utf8</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> cleanToken </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> tokenString.</span><span style="color: #88C0D0">replacingOccurrences</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">of</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #EBCB8B">\&quot;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">with</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            urlRequestToUse.</span><span style="color: #88C0D0">setValue</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Bearer </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">cleanToken</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">forHTTPHeaderField</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Authorization</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">completion</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">success</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">urlRequestToUse</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>Every request automatically gets the Bearer token added &#8211; no need to remember to add authentication headers manually.</p>



<h3 class="wp-block-heading">Smart Retry Logic</h3>



<p>The retry mechanism is where this API manager really shines:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
    let requestKey = &#8220;\(request.id)&#8221;
    
    Task {
        let currentAttempts = await attemptTracker.getAttempts(for: requestKey)
        
        if currentAttempts >= 3 {
            await attemptTracker.removeAttempts(for: requestKey)
            completion(.doNotRetry)
            return
        }
        
        if let response = request.task?.response as? HTTPURLResponse {
            switch response.statusCode {
            case 400&#8230;499:
                // Client errors &#8211; don&#8217;t retry
                completion(.doNotRetryWithError(error))
            case 500&#8230;599:
                // Server errors &#8211; retry after delay
                await attemptTracker.incrementAttempts(for: requestKey)
                completion(.retryWithDelay(3))
            default:
                break
            }
        }
        
        await attemptTracker.incrementAttempts(for: requestKey)
        completion(.retryWithDelay(3))
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">retry</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">request</span><span style="color: #D8DEE9FF">: Request, </span><span style="color: #88C0D0">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">session</span><span style="color: #D8DEE9FF">: Session, </span><span style="color: #88C0D0">dueTo</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">Error</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">completion</span><span style="color: #D8DEE9FF">: </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">escaping</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">RetryResult</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Void</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> requestKey </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">request.</span><span style="color: #D8DEE9">id</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> currentAttempts </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> attemptTracker.</span><span style="color: #88C0D0">getAttempts</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> requestKey</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> currentAttempts </span><span style="color: #81A1C1">&gt;=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> attemptTracker.</span><span style="color: #88C0D0">removeAttempts</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> requestKey</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">completion</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">doNotRetry</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">return</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> response </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> request.task</span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF">.response </span><span style="color: #81A1C1">as?</span><span style="color: #D8DEE9FF"> HTTPURLResponse </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">switch</span><span style="color: #D8DEE9FF"> response.statusCode </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">400</span><span style="color: #81A1C1">...</span><span style="color: #B48EAD">499</span><span style="color: #81A1C1">:</span></span>
<span class="line"><span style="color: #ECEFF4">                </span><span style="color: #616E88">// Client errors - don&#39;t retry</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">completion</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">doNotRetryWithError</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">error</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">500</span><span style="color: #81A1C1">...</span><span style="color: #B48EAD">599</span><span style="color: #81A1C1">:</span></span>
<span class="line"><span style="color: #ECEFF4">                </span><span style="color: #616E88">// Server errors - retry after delay</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> attemptTracker.</span><span style="color: #88C0D0">incrementAttempts</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> requestKey</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">completion</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">retryWithDelay</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">default:</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">break</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> attemptTracker.</span><span style="color: #88C0D0">incrementAttempts</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> requestKey</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">completion</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">retryWithDelay</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The logic is smart: client errors (4xx) don&#8217;t get retried because they&#8217;re usually permanent, but server errors (5xx) do get retried since servers can recover.</p>



<h3 class="wp-block-heading">Thread-Safe Attempt Tracking</h3>



<p>To track retries safely across concurrent requests, we use an&nbsp;<code>actor</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>actor RequestAttemptTracker {
    private var attempts: [String: Int] = [:]
    
    func getAttempts(for key: String) -> Int {
        attempts[key] ?? 0
    }
    
    func incrementAttempts(for key: String) {
        let current = attempts[key] ?? 0
        attempts[key] = current + 1
    }
    
    func removeAttempts(for key: String) {
        attempts.removeValue(forKey: key)
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">actor</span><span style="color: #D8DEE9FF"> RequestAttemptTracker </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> attempts: </span><span style="color: #ECEFF4">[</span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Int</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF">]</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">getAttempts</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">key</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Int</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        attempts</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">key</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">??</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">incrementAttempts</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">key</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> current </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> attempts</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">key</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">??</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span></span>
<span class="line"><span style="color: #D8DEE9FF">        attempts</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">key</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> current </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">removeAttempts</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">key</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        attempts.</span><span style="color: #88C0D0">removeValue</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">forKey</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> key</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The&nbsp;<code>actor</code>&nbsp;keyword ensures thread-safe access to our attempts dictionary &#8211; crucial for handling multiple concurrent network requests.</p>



<h3 class="wp-block-heading">Structured Error Handling</h3>



<p>Finally, define clear error types:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>enum APIError: Error {
    case invalidURL
    case networkError(String)
    case httpError(statusCode: Int, message: String)
    case decodingError
    case noData
    case unauthorized
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">enum</span><span style="color: #D8DEE9FF"> APIError</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">Error </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">invalidURL</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">networkError</span><span style="color: #ECEFF4">(</span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">httpError</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">statusCode</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Int</span><span style="color: #D8DEE9FF">, </span><span style="color: #D8DEE9">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">decodingError</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">noData</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">unauthorized</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">Using Your API Manager</h3>



<p>With everything in place, making requests is simple &#8211; we will just update the standard ContentView to display the data raw without any parsing:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import SwiftUI

struct ContentView: View {
    @State private var message = &#8220;Hello, world!&#8221;
    
    var body: some View {
        VStack {
            Image(systemName: &#8220;globe&#8221;)
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(message)
            
            Button(&#8220;Fetch Data&#8221;) {
                fetchData()
            }
        }
        .padding()
    }
    
    private func fetchData() {
        APIManager.shared.execute(
            serverUrl: &#8220;https://jsonplaceholder.typicode.com/posts&#8221;,
            httpBody: nil,
            success: { response in
                DispatchQueue.main.async {
                    self.message = response.value ?? &#8220;No data&#8221;
                }
            },
            failure: { error in
                DispatchQueue.main.async {
                    self.message = &#8220;Error occurred&#8221;
                }
            }
        )
    }
}
)</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ContentView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> message </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Hello, world!</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">globe</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">imageScale</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">large</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">foregroundStyle</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">tint</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">message</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Fetch Data</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">fetchData</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetchData</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        APIManager.</span><span style="color: #D8DEE9">shared</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">serverUrl</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">https://jsonplaceholder.typicode.com/posts</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">httpBody</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">nil</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">success</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> response </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                DispatchQueue.</span><span style="color: #D8DEE9">main</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">message</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> response.value </span><span style="color: #81A1C1">??</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">No data</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">failure</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> error </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                DispatchQueue.</span><span style="color: #D8DEE9">main</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">message</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Error occurred</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span></code></pre></div>



<p>That code will produce something like this:</p>



<figure class="wp-block-image aligncenter size-large"><img decoding="async" width="471" height="1024" src="https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-23-at-14.08.13-471x1024.png" alt="" class="wp-image-843" srcset="https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-23-at-14.08.13-471x1024.png 471w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-23-at-14.08.13-138x300.png 138w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-23-at-14.08.13-768x1670.png 768w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-23-at-14.08.13-706x1536.png 706w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-23-at-14.08.13-942x2048.png 942w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-23-at-14.08.13-scaled.png 1177w" sizes="(max-width: 471px) 100vw, 471px" /></figure>



<h2 class="wp-block-heading">Cost-Benefit Analysis</h2>



<p>While adding any external dependency requires consideration, the benefits of Alamofire far outweigh the costs:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th><strong>Cost Factor</strong></th><th><strong>Alamofire Impact</strong></th><th><strong>Net Benefit</strong></th></tr><tr><td>Development Time</td><td>Faster feature implementation, fewer networking bugs</td><td>50-70% reduction in networking code volume</td></tr><tr><td>Maintenance Overhead</td><td>Less custom networking code to maintain and debug</td><td>Focus resources on core app features</td></tr><tr><td>Code Quality</td><td>More readable, testable networking implementations</td><td>Improved team productivity and onboarding</td></tr><tr><td>Security Implementation</td><td>Built-in security features vs. custom development</td><td>Enterprise-grade security without specialist knowledge</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Conclusion: Embracing Alamofire for Modern iOS Development</h2>



<p>Understanding&nbsp;<strong>how to use Alamofire in Swift 2025</strong>&nbsp;is about more than just making HTTP requests—it&#8217;s about building a robust, maintainable networking foundation that can evolve with your application&#8217;s needs. The&nbsp;<strong>Alamofire best practices iOS</strong>&nbsp;we&#8217;ve discussed demonstrate why this&nbsp;<strong>Swift networking library</strong>&nbsp;continues to be the preferred choice for professional iOS development.</p>



<p>Whether you&#8217;re building a simple app with basic API integration or a complex enterprise application with sophisticated networking requirements, implementing an&nbsp;<strong>Alamofire iOS tutorial</strong>&nbsp;approach will serve your project well. The investment in learning Alamofire&#8217;s patterns and capabilities pays dividends throughout your application&#8217;s lifecycle, enabling you to focus on building great features rather than wrestling with networking complexity.</p>



<h2 class="wp-block-heading">Get the Complete Code</h2>



<p><strong><a href="https://github.com/AndersSoftware/Alamofire-Tutorial/tree/main">Download the full source code on GitHub →</a></strong></p>



<p></p>
<p>The post <a href="https://softwareanders.com/alamofire-swift-tutorial-build-robust-ios-networking-with-less-code/">Alamofire Swift Tutorial: Build Robust iOS Networking with Less Code</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/alamofire-swift-tutorial-build-robust-ios-networking-with-less-code/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI Supabase Authentication: Complete Tutorial</title>
		<link>https://softwareanders.com/swiftui-supabase-authentication-complete-tutorial/</link>
					<comments>https://softwareanders.com/swiftui-supabase-authentication-complete-tutorial/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Thu, 12 Jun 2025 11:17:34 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=803</guid>

					<description><![CDATA[<p>Building secure&#160;iOS authentication&#160;for your SwiftUI apps just got easier. This&#160;SwiftUI Supabase tutorial&#160;shows you how to implement a complete login system using Supabase Auth—the open-source Firebase alternative that&#8217;s transforming mobile app development in 2025. Whether you&#8217;re building your first&#160;iOS app authentication&#160;system or migrating from Firebase, this&#160;Supabase Swift integration guide&#160;provides everything you need. You&#8217;ll learn to create [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/swiftui-supabase-authentication-complete-tutorial/">SwiftUI Supabase Authentication: Complete Tutorial</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>Building secure&nbsp;<strong>iOS authentication</strong>&nbsp;for your SwiftUI apps just got easier. This&nbsp;<strong>SwiftUI Supabase tutorial</strong>&nbsp;shows you how to implement a complete login system using Supabase Auth—the open-source Firebase alternative that&#8217;s transforming mobile app development in 2025.</p>



<p></p>



<p>Whether you&#8217;re building your first&nbsp;<strong>iOS app authentication</strong>&nbsp;system or migrating from Firebase, this&nbsp;<strong>Supabase Swift integration guide</strong>&nbsp;provides everything you need. You&#8217;ll learn to create secure sign-in, registration, and session management with clean, production-ready SwiftUI code.</p>



<p>Unlike basic authentication guides, we&#8217;ll build a complete system with proper error handling, input validation, and real-world best practices that you can deploy immediately.</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="471" height="1024" src="https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-12-at-13.15.55-471x1024.png" alt="" class="wp-image-831" srcset="https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-12-at-13.15.55-471x1024.png 471w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-12-at-13.15.55-138x300.png 138w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-12-at-13.15.55-768x1670.png 768w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-12-at-13.15.55-706x1536.png 706w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-12-at-13.15.55-942x2048.png 942w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-12-at-13.15.55-scaled.png 1177w" sizes="(max-width: 471px) 100vw, 471px" /></figure>



<h2 class="wp-block-heading">What You&#8217;ll Learn</h2>



<p>By the end of this tutorial, you&#8217;ll have a fully functional authentication system that includes:</p>



<ul class="wp-block-list">
<li>User registration with email verification</li>



<li>Secure sign-in functionality</li>



<li>Password reset capabilities</li>



<li>Session management</li>



<li>Error handling and user feedback</li>



<li>Clean, reusable SwiftUI components</li>
</ul>



<h2 class="wp-block-heading">Prerequisites</h2>



<p>Before we dive in, make sure you have:</p>



<ul class="wp-block-list">
<li>Xcode 14+ installed</li>



<li>Basic knowledge of SwiftUI</li>



<li>A Supabase account (free tier available)</li>



<li>iOS 15+ as your deployment target</li>
</ul>



<h2 class="wp-block-heading">Setting Up Your Supabase Project</h2>



<p>First, let&#8217;s set up the backend infrastructure:</p>



<h3 class="wp-block-heading">1. Create a Supabase Project</h3>



<ol class="wp-block-list">
<li>Visit&nbsp;<a href="https://supabase.com/">supabase.com</a>&nbsp;and create a new account</li>



<li>Click &#8220;New Project&#8221; and fill in your project details</li>



<li>Wait for your database to initialize (usually takes 2-3 minutes)</li>



<li>Find your API URL which can be found in&nbsp;Project Settings -&gt; Data API&nbsp;</li>



<li>Find your API KEY which also can be found in&nbsp;Project<code> </code>Settings -&gt;<code>&nbsp;</code>API&nbsp;KEYS</li>
</ol>



<h3 class="wp-block-heading">2. Configure Authentication Settings</h3>



<p>To simplify this tutorial we are going to disable &#8220;Confirm email&#8221; &#8211; when you go to production, it&#8217;s good practice to keep this enabled and set it up correctly. But this tutorial is focused on implementing Supabase inside a iOS application:</p>



<ol class="wp-block-list">
<li>Go to Authentication &gt; Sign In / Providers</li>



<li>Click on Email</li>



<li>Disable Confirm email</li>
</ol>



<h2 class="wp-block-heading">iOS Project Setup with Supabase</h2>



<p>Before we can do anything we need to create a new App, so open Xcode and create a new iOS project with SwiftUI interface.</p>



<h3 class="wp-block-heading">Implement Supabase</h3>



<p>Now it’s time to add Supabase to our project. Luckily this is pretty straightforward and Supabase have created an excellent package we will use.</p>



<p>In Xcode, go to&nbsp;<strong>File → Add Package Dependencies</strong>&nbsp;and add:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>https://github.com/supabase/supabase-swift</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">https</span><span style="color: #81A1C1">:</span><span style="color: #616E88">//github.com/supabase/supabase-swift</span></span></code></pre></div>



<p>Then just add the packages to your project.</p>



<h3 class="wp-block-heading">Create Supabase service</h3>



<p>This Swift code creates a singleton service class that manages the Supabase client connection for your iOS app. </p>



<p>The&nbsp;<code>SupabaseService</code>&nbsp;class acts as a central hub for all Supabase operations, ensuring there&#8217;s only one client instance throughout your app&#8217;s lifecycle. </p>



<p>This approach provides better resource management and consistency when handling authentication, database queries, and other Supabase features.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Supabase
import Foundation

class SupabaseService {
    static let shared = SupabaseService()
    
    let client: SupabaseClient
    
    private init() {
        // Replace with your actual Supabase credentials
        guard let url = URL(string: &#8220;**ENTER YOUR SUPABASE URL**&#8221;) else {
            fatalError(&#8220;Invalid Supabase URL&#8221;)
        }
        
        self.client = SupabaseClient(
            supabaseURL: url,
            supabaseKey: &#8220;**ENTER YOUR SUPABASE API KEY**&#8221;
        )
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Supabase</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">SupabaseService</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> shared </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">SupabaseService</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> client: SupabaseClient</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Replace with your actual Supabase credentials</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">guard</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> url </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">URL</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">string</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">**ENTER YOUR SUPABASE URL**</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">fatalError</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Invalid Supabase URL</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">client</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">SupabaseClient</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">supabaseURL</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> url,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">supabaseKey</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">**ENTER YOUR SUPABASE API KEY**</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<ul class="wp-block-list">
<li><code>static let shared = SupabaseService()</code>&nbsp;&#8211; Singleton pattern for app-wide access</li>



<li><code>let client: SupabaseClient</code>&nbsp;&#8211; The main Supabase client instance</li>



<li><code>private init()</code>&nbsp;&#8211; Prevents external instantiation, enforcing singleton pattern</li>
</ul>



<p>The initializer:</p>



<ol class="wp-block-list">
<li>Creates a URL from the Supabase project URL string</li>



<li>Uses&nbsp;<code>guard let</code>&nbsp;to safely unwrap the URL or crash with&nbsp;<code>fatalError</code></li>



<li>Initializes the&nbsp;<code>SupabaseClient</code>&nbsp;with the project URL and anonymous API key</li>
</ol>



<p>This service acts as a centralized access point for Supabase operations throughout the app. Other classes (like&nbsp;<code>AuthManager</code>) can access the client via <code>SupabaseService.shared.client</code>.</p>



<h3 class="wp-block-heading">Using Supabase in AuthManager</h3>



<p>Now it&#8217;s time to create a AuthManager to use our Supabase service and then create the rest of the functions.</p>



<p> As an&nbsp;<code>ObservableObject</code>, it manages authentication state and automatically updates the UI when that state changes. </p>



<p>The class provides a complete authentication system including sign-in, registration, sign-out, and password reset functionality.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Foundation
import Auth

class AuthManager: ObservableObject {
    @Published var isSignedIn: Bool = false
    @Published var currentUser: User?
    
    private let supabase = SupabaseService.shared.client
    
    init() {
        Task {
            await checkAuthStatus()
        }
    }
    
    @MainActor
    func checkAuthStatus() async {
        do {
            let session = try await supabase.auth.session
            let user = session.user
            self.currentUser = user
            self.isSignedIn = true
        } catch {
            print(&#8220;No active session: \(error)&#8221;)
            self.isSignedIn = false
            self.currentUser = nil
        }
    }
    
    func resetPassword(email: String) async throws {
        try await supabase.auth.resetPasswordForEmail(email)
    }
    
    func signOut() async {
        do {
            try await supabase.auth.signOut()
            self.currentUser = nil
            self.isSignedIn = false
        } catch {
            print(&#8220;Sign out error: \(error)&#8221;)
        }
    }
    
    func signIn(email: String, password: String) async throws {
        let response = try await supabase.auth.signIn(
            email: email,
            password: password
        )
        
        self.currentUser = response.user
        self.isSignedIn = true
    }
    
    @MainActor
    func register(email: String, password: String) async throws {
        let response = try await supabase.auth.signUp(
            email: email,
            password: password
        )
        
        self.currentUser = response.user
        self.isSignedIn = true
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Auth</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">AuthManager</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ObservableObject </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Published</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isSignedIn: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Published</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> currentUser: User</span><span style="color: #81A1C1">?</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> supabase </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> SupabaseService.</span><span style="color: #D8DEE9">shared</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">client</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">checkAuthStatus</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">MainActor</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">checkAuthStatus</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> session </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabase.</span><span style="color: #D8DEE9">auth</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">session</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> user </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> session.</span><span style="color: #D8DEE9">user</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">currentUser</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> user</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">No active session: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">currentUser</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">resetPassword</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabase.</span><span style="color: #D8DEE9">auth</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">resetPasswordForEmail</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">signOut</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabase.</span><span style="color: #D8DEE9">auth</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">signOut</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">currentUser</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">nil</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Sign out error: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">signIn</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">password</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> response </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabase.</span><span style="color: #D8DEE9">auth</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">signIn</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">email</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> password</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">currentUser</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> response.</span><span style="color: #D8DEE9">user</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">MainActor</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">register</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">password</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> response </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabase.</span><span style="color: #D8DEE9">auth</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">signUp</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">email</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> password</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">currentUser</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> response.</span><span style="color: #D8DEE9">user</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isSignedIn</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<ul class="wp-block-list">
<li><code>ObservableObject</code>&nbsp;protocol enables SwiftUI reactivity</li>



<li><code>@Published var isSignedIn: Bool</code>&nbsp;&#8211; Tracks authentication state</li>



<li><code>@Published var currentUser: User?</code>&nbsp;&#8211; Stores current user data</li>



<li><code>private let supabase</code>&nbsp;&#8211; Reference to the shared Supabase client</li>
</ul>



<p>The&nbsp;<code>init()</code>&nbsp;method automatically calls&nbsp;<code>checkAuthStatus()</code>&nbsp;to verify if a user session exists when the app starts. The&nbsp;<code>@MainActor</code>&nbsp;decorator ensures UI updates happen on the main thread.</p>



<p><strong>checkAuthStatus()</strong>&nbsp;&#8211; Retrieves existing session and updates authentication state&nbsp;</p>



<p><strong>signIn()</strong>&nbsp;&#8211; Authenticates user with email/password and updates state&nbsp;</p>



<p><strong>register()</strong>&nbsp;&#8211; Creates new user account and signs them in&nbsp;</p>



<p><strong>signOut()</strong>&nbsp;&#8211; Ends current session and resets authentication state&nbsp;</p>



<p><strong>resetPassword()</strong>&nbsp;&#8211; Sends password reset email via Supabase</p>



<p>Methods use&nbsp;<code>async throws</code>&nbsp;for proper error propagation, while sign-out includes internal error handling to prevent app crashes. The&nbsp;<code>@MainActor</code>&nbsp;decorators ensure UI-affecting operations run on the main thread.</p>



<h2 class="wp-block-heading">Create AUTH flow and SwiftUI Views</h2>



<p>The flow in our app will be: Open the app, the app checks if the user is logged in or not. If the user is logged in to user will see the HomeView and if not logged in the user will see the SignInView. On the SignInView there will be a button that leads the user to RegisterView.</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="600" height="500" src="https://softwareanders.com/wp-content/uploads/2025/06/app_auth_flow_svg-2.png" alt="" class="wp-image-806" srcset="https://softwareanders.com/wp-content/uploads/2025/06/app_auth_flow_svg-2.png 600w, https://softwareanders.com/wp-content/uploads/2025/06/app_auth_flow_svg-2-300x250.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></figure>



<p>In order for us to create the flow above, we need a class that knows if the user is logged in or not &#8211; we already made that and it&#8217;s called AuthManager, now it&#8217;s time to create the views we need and use the AuthManager</p>



<h3 class="wp-block-heading">Building the views</h3>



<p>Next up we are going to create the views for the app. We will have four views: ContentView, HomeView, RegisterView and SignInView.</p>



<p>Pro tip: Create a folder called Views and place your views inside the folder.</p>



<h4 class="wp-block-heading">SignInView.swift</h4>



<p>This SwiftUI code creates a sign-in screen for Supabase authentication. Here&#8217;s the breakdown:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import SwiftUI

struct SignInView: View {
    @EnvironmentObject var authManager: AuthManager
    @State private var email: String = &#8220;&#8221;
    @State private var password: String = &#8220;&#8221;
    
    var body: some View {
        NavigationStack {
            
        
        VStack(spacing: 40) {
            Text(&#8220;Supabase AUTH tutorial&#8221;)
                .font(.title)
                .bold()
            
            
            VStack(alignment: .leading, spacing: 20) {
                VStack(alignment: .leading, spacing: 3) {
                    Text(&#8220;Enter your email below:&#8221;)
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                    TextField(&#8220;Enter your email&#8221;, text: $email)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                }
              
                VStack(alignment: .leading, spacing: 3) {
                    Text(&#8220;Enter your password below:&#8221;)
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                    SecureField(&#8220;Enter your password&#8221;, text: $password)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                }
                    
            }
            
            VStackLayout(spacing: 10) {
                Button(action: {
                    Task {
                        do {
                            try await authManager.signIn(email: email, password: password)
                        } catch {
                            print(&#8220;Login failed: \(error.localizedDescription)&#8221;)
                        }
                    }
                }) {
                    Text(&#8220;Login using Supabase&#8221;)
                        .font(.headline)
                        .foregroundColor(.white)
                        .padding()
                        .background(Color.blue)
                        .cornerRadius(8)
                }
                
                NavigationLink(destination:
                                RegisterView()
                                    .environmentObject(authManager)
                ) {
                    Text(&#8220;Register&#8221;)
                        .font(.subheadline)
                }
                
            }
        }
        .padding()
        }
    }
}

#Preview {
    SignInView()
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> SignInView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">EnvironmentObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager: AuthManager</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> email: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> password: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">NavigationStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">40</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Supabase AUTH tutorial</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">20</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your email below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your email</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your password below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">SecureField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">VStackLayout</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">10</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">action</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> authManager.</span><span style="color: #88C0D0">signIn</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email, </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Login failed: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error.</span><span style="color: #D8DEE9">localizedDescription</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Login using Supabase</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">headline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">blue</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">NavigationLink</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">destination</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                </span><span style="color: #88C0D0">RegisterView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                    .</span><span style="color: #88C0D0">environmentObject</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">authManager</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Register</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">SignInView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Key Components</p>



<ul class="wp-block-list">
<li><code>@EnvironmentObject var authManager: AuthManager</code>&nbsp;&#8211; Shared authentication manager</li>



<li><code>@State</code>&nbsp;variables for email and password input</li>



<li><code>NavigationStack</code>&nbsp;for screen navigation</li>
</ul>



<p>The layout uses nested&nbsp;<code>VStack</code>&nbsp;containers with:</p>



<ul class="wp-block-list">
<li>Title text</li>



<li>Email&nbsp;<code>TextField</code>&nbsp;and password&nbsp;<code>SecureField</code>&nbsp;with labels</li>



<li>Login button that calls&nbsp;<code>authManager.signIn()</code>&nbsp;in an async&nbsp;<code>Task</code></li>



<li><code>NavigationLink</code>&nbsp;to the registration screen</li>
</ul>



<p>When &#8220;Login using Supabase&#8221; is tapped:</p>



<ol class="wp-block-list">
<li>Creates async task</li>



<li>Calls&nbsp;<code>authManager.signIn(email:password:)</code></li>



<li>Handles errors with&nbsp;<code>do-catch</code><span style="font-size: revert;"></span></li>
</ol>



<h4 class="wp-block-heading">RegisterView.swift</h4>



<p>This code creates a registration screen with great user experience including loading states, error handling, and user feedback alerts:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import SwiftUI

struct RegisterView: View {
    @EnvironmentObject var authManager: AuthManager

    @State private var email: String = &#8220;&#8221;
    @State private var password: String = &#8220;&#8221;
    @State private var confirmPassword: String = &#8220;&#8221;
    @State private var isLoading = false
    @State private var showError = false
    @State private var errorMessage: String = &#8220;&#8221;
    
    var body: some View {
        VStack(spacing: 40) {
            Text(&#8220;Register to Supabase&#8221;)
                .font(.title)
                .bold()
            
            
            VStack(alignment: .leading, spacing: 20) {
                VStack(alignment: .leading) {
                    Text(&#8220;Enter email below:&#8221;)
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                    TextField(&#8220;Enter email&#8221;, text: $email)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                }
              
                VStack(alignment: .leading) {
                    Text(&#8220;Enter password below:&#8221;)
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                    SecureField(&#8220;Enter password&#8221;, text: $password)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                    SecureField(&#8220;Re-enter password&#8221;, text: $confirmPassword)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                }
                    
            }
            Button(action: {
                Task {
                    do {
                        isLoading = true
                        try await authManager.register(email: email, password: password)
                        isLoading = false
                    }catch {
                        isLoading = false
                        errorMessage = error.localizedDescription
                        print(&#8220;Registration failed: \(error.localizedDescription)&#8221;)
                        showError = true
                    }
                }
            }) {
                HStack {
                    if isLoading {
                        ProgressView()
                            .progressViewStyle(CircularProgressViewStyle(tint: .white))
                            .scaleEffect(0.8)
                    } else {
                        Text(&#8220;Signup using Supabase&#8221;)
                    }
                }
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .background(Color.blue)
                    .cornerRadius(8)
            }
        }
        .alert(&#8220;Registration failed!&#8221;, isPresented: $showError) {
            Button(&#8220;OK&#8221;) {
                showError = false
            }
        } message: {
            Text(&#8220;\(errorMessage)&#8221;)
        }
        .padding()
    }
}

#Preview {
    RegisterView()
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> RegisterView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">EnvironmentObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager: AuthManager</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> email: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> password: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> confirmPassword: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> showError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> errorMessage: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">40</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Register to Supabase</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">20</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter email below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter email</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter password below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">SecureField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">SecureField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Re-enter password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $confirmPassword</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">action</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> authManager.</span><span style="color: #88C0D0">register</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email, </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> password</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        errorMessage </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> error.</span><span style="color: #D8DEE9">localizedDescription</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Registration failed: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error.</span><span style="color: #D8DEE9">localizedDescription</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        showError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">HStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> isLoading </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">ProgressView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">progressViewStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">CircularProgressViewStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">tint</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">scaleEffect</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0.8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Signup using Supabase</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">headline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">blue</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">alert</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Registration failed!</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isPresented</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $showError</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">OK</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                showError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">errorMessage</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">RegisterView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<ul class="wp-block-list">
<li><code>@EnvironmentObject var authManager: AuthManager</code>&nbsp;&#8211; Shared authentication manager</li>



<li><code>@State</code>&nbsp;variables for email, password, and confirmPassword input</li>



<li><code>@State private var isLoading: Bool</code>&nbsp;&#8211; Loading state for button</li>



<li><code>@State private var showError: Bool</code>&nbsp;&#8211; Error alert trigger</li>



<li><code>@State private var errorMessage: String</code>&nbsp;&#8211; Stores error details for display</li>
</ul>



<p>The layout uses&nbsp;<code>VStack</code>&nbsp;containers with:</p>



<ul class="wp-block-list">
<li>&#8220;Register to Supabase&#8221; title</li>



<li>Email&nbsp;<code>TextField</code>&nbsp;with label</li>



<li>Two password&nbsp;<code>SecureField</code>&nbsp;inputs (password and confirmation) with labels</li>



<li>Smart signup button that shows loading indicator or text</li>



<li>Error alert dialog for registration failures</li>
</ul>



<p>The signup button now includes full implementation:</p>



<ol class="wp-block-list">
<li><strong>Loading State</strong>: The signup button shows a&nbsp;<code>ProgressView</code>&nbsp;spinner during registration and displays appropriate text or loading indicator.</li>



<li><strong>Error Handling</strong>: Comprehensive error management that captures error messages and displays them in user-friendly alerts.</li>



<li><strong>Visual Feedback</strong>: Loading spinner provides immediate feedback that the registration process is active.</li>
</ol>



<h4 class="wp-block-heading">HomeView.swift</h4>



<p>This SwiftUI code creates a simple home screen shown after successful authentication. Here&#8217;s the breakdown:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import SwiftUI

struct HomeView: View {
    @EnvironmentObject var authManager: AuthManager

    var body: some View {
        Text(&#8220;Hello, World!&#8221;)
        
        Button {
            Task {
                await authManager.signOut()
            }
        
        }label: {
            Text(&#8220;Sign Out&#8221;)
        }
    }
}

#Preview {
    HomeView()
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> HomeView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">EnvironmentObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager: AuthManager</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Hello, World!</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Button</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> authManager.</span><span style="color: #88C0D0">signOut</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Sign Out</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">HomeView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<ul class="wp-block-list">
<li><code>@EnvironmentObject var authManager: AuthManager</code>&nbsp;&#8211; Shared authentication manager</li>



<li>Basic UI with placeholder content and sign-out button</li>
</ul>



<p>The layout contains:</p>



<ul class="wp-block-list">
<li>Sign-out button with complete logout functionality</li>
</ul>



<p>The sign-out button now includes full implementation:</p>



<ol class="wp-block-list">
<li>Creates an async&nbsp;<code>Task</code></li>



<li>Calls&nbsp;<code>authManager.signOut()</code>&nbsp;to end the user session</li>



<li>The&nbsp;<code>AuthManager</code>&nbsp;handles the logout process and automatically updates authentication state</li>



<li>SwiftUI reactively switches back to the sign-in screen when&nbsp;<code>isSignedIn</code>&nbsp;becomes false</li>
</ol>



<h4 class="wp-block-heading">ContentView.swift</h4>



<p>This SwiftUI code creates the main app controller that manages authentication state. Here&#8217;s the breakdown:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import SwiftUI
struct ContentView: View {
    @StateObject private var authManager = AuthManager()
    
    var body: some View {
        if authManager.isSignedIn {
            HomeView()
                .environmentObject(authManager)
        }else {
            SignInView()
                .environmentObject(authManager)
        }
    }
}
#Preview {
    ContentView()
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ContentView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">StateObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">AuthManager</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> authManager.isSignedIn </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">HomeView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">environmentObject</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">authManager</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">SignInView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">environmentObject</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">authManager</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ContentView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<ul class="wp-block-list">
<li><code>@StateObject private var authManager = AuthManager()</code>&nbsp;&#8211; Creates and owns the authentication manager instance</li>



<li>Conditional view rendering based on authentication state</li>
</ul>



<p>The view uses a simple&nbsp;<code>if-else</code>&nbsp;statement to determine which screen to show:</p>



<ul class="wp-block-list">
<li><strong>If signed in</strong>: Shows&nbsp;<code>HomeView()</code></li>



<li><strong>If not signed in</strong>: Shows&nbsp;<code>SignInView()</code></li>
</ul>



<p>Both views receive the&nbsp;<code>authManager</code>&nbsp;as an environment object using&nbsp;<code>.environmentObject(authManager)</code>.</p>



<p>This acts as the root view that automatically switches between the authentication flow and the main app content based on the user&#8217;s login status. </p>



<p>When&nbsp;<code>authManager.isSignedIn</code>&nbsp;changes, SwiftUI automatically updates the displayed view.</p>



<h4 class="wp-block-heading">ForgotPasswordView.swift</h4>



<p>The following code creates a password reset screen with enhanced user experience features including loading states, success feedback, and proper form validation.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import SwiftUI

struct ForgotPasswordView: View {
    @EnvironmentObject var authManager: AuthManager
    @State private var email: String = &#8220;&#8221;
    @State private var isLoading = false
    @State private var showSuccess = false
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        NavigationStack {
            VStack(spacing: 40) {
                Text(&#8220;Forgot Password&#8221;)
                    .font(.title)
                    .bold()
                
                VStack(alignment: .leading, spacing: 20) {
                    VStack(alignment: .leading, spacing: 3) {
                        Text(&#8220;Enter your email below:&#8221;)
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                        TextField(&#8220;Enter your email&#8221;, text: $email)
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                            .keyboardType(.emailAddress)
                            .autocapitalization(.none)
                    }
                }
                
                VStackLayout(spacing: 10) {
                    Button(action: {
                        Task {
                            isLoading = true
                            try? await authManager.resetPassword(email: email)
                            showSuccess = true
                            isLoading = false
                        }
                    }) {
                        HStack {
                            if isLoading {
                                ProgressView()
                                    .progressViewStyle(CircularProgressViewStyle(tint: .white))
                                    .scaleEffect(0.8)
                            } else {
                                Text(&#8220;Send Reset Link&#8221;)
                            }
                        }
                        .font(.headline)
                        .foregroundColor(.white)
                        .padding()
                        .background(Color.blue)
                        .cornerRadius(8)
                    }
                    .disabled(email.isEmpty || isLoading)
                    
                    Button(&#8220;Back to Sign In&#8221;) {
                        dismiss()
                    }
                    .font(.subheadline)
                }
            }
            .padding()
            .alert(&#8220;Reset Link Sent!&#8221;, isPresented: $showSuccess) {
                Button(&#8220;OK&#8221;) {
                    dismiss()
                }
            } message: {
                Text(&#8220;We&#8217;ve sent a password reset link to \(email). Please check your inbox.&#8221;)
            }
        }
    }
}

#Preview {
    ForgotPasswordView()
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ForgotPasswordView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">EnvironmentObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> authManager: AuthManager</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> email: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> showSuccess </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Environment</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">\.</span><span style="color: #D8DEE9">dismiss</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> dismiss</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">NavigationStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">40</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Forgot Password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">20</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">VStack</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">alignment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">leading</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">3</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your email below:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">secondary</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Enter your email</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">keyboardType</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">emailAddress</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">autocapitalization</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.none</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">VStackLayout</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">spacing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">10</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">action</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #81A1C1">try?</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> authManager.</span><span style="color: #88C0D0">resetPassword</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">email</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> email</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            showSuccess </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            isLoading </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">HStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> isLoading </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                </span><span style="color: #88C0D0">ProgressView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                    .</span><span style="color: #88C0D0">progressViewStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">CircularProgressViewStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">tint</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                    .</span><span style="color: #88C0D0">scaleEffect</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0.8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                                </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Send Reset Link</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">headline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">blue</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">disabled</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">email.isEmpty </span><span style="color: #81A1C1">||</span><span style="color: #D8DEE9FF"> isLoading</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Back to Sign In</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">dismiss</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">subheadline</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">alert</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Reset Link Sent!</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isPresented</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $showSuccess</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">OK</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">dismiss</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">We&#39;ve sent a password reset link to </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">email</span><span style="color: #81A1C1">)</span><span style="color: #A3BE8C">. Please check your inbox.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">#Preview</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ForgotPasswordView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<ul class="wp-block-list">
<li><code>@EnvironmentObject var authManager: AuthManager</code>&nbsp;&#8211; Shared authentication manager</li>



<li><code>@State private var email: String</code>&nbsp;&#8211; Email input field</li>



<li><code>@State private var isLoading: Bool</code>&nbsp;&#8211; Loading state for button</li>



<li><code>@State private var showSuccess: Bool</code>&nbsp;&#8211; Success alert trigger</li>



<li><code>@Environment(\.dismiss)</code>&nbsp;&#8211; SwiftUI environment value to dismiss the view</li>
</ul>



<p>The layout includes:</p>



<ul class="wp-block-list">
<li>&#8220;Forgot Password&#8221; title</li>



<li>Email input field with proper keyboard type and capitalization settings</li>



<li>Smart reset button that shows loading indicator or text</li>



<li>&#8220;Back to Sign In&#8221; button for navigation</li>



<li>Success alert dialog</li>
</ul>



<p><strong>Loading State</strong>: The reset button shows a&nbsp;<code>ProgressView</code>&nbsp;spinner while processing and is disabled during the operation.</p>



<p><strong>Form Validation</strong>: The button is disabled when the email field is empty or during loading.</p>



<p><strong>Success Feedback</strong>: An alert appears when the reset email is sent, providing clear user confirmation.</p>



<p><strong>Keyboard Optimization</strong>: Email field uses&nbsp;<code>.emailAddress</code>&nbsp;keyboard type and disables auto-capitalization.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p>All done! You&#8217;ve built a complete, production-ready authentication system with SwiftUI and Supabase that includes secure registration, sign-in, password reset, and session management.</p>



<h2 class="wp-block-heading">Get the Complete Code</h2>



<p><strong><a href="https://github.com/AndersSoftware/SupabaseAuthTutorial/tree/main">Download the full source code on GitHub →</a></strong></p>



<p><strong>What will you build next?</strong>&nbsp;This authentication foundation is ready for any iOS app idea &#8211; from social platforms to productivity tools. Share your projects in the comments!</p>



<p></p>
<p>The post <a href="https://softwareanders.com/swiftui-supabase-authentication-complete-tutorial/">SwiftUI Supabase Authentication: Complete Tutorial</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/swiftui-supabase-authentication-complete-tutorial/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI ToDo App Tutorial: Integrating Supabase for Backend Support</title>
		<link>https://softwareanders.com/building-a-complete-todo-app-with-swiftui-and-supabase/</link>
					<comments>https://softwareanders.com/building-a-complete-todo-app-with-swiftui-and-supabase/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Mon, 02 Jun 2025 10:25:57 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[iOS app development]]></category>
		<category><![CDATA[SwiftUI backend integration]]></category>
		<category><![CDATA[SwiftUI todo app]]></category>
		<category><![CDATA[SwiftUI tutorial]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=726</guid>

					<description><![CDATA[<p>In this comprehensive SwiftUI tutorial for iOS developers, we&#8217;ll build a fully functional todo application using SwiftUI for iOS development and Supabase as our open-source backend service. By the end of this step-by-step guide, you&#8217;ll have created a production-ready iOS app with real-time database synchronization and complete CRUD operations. We&#8217;ll start by creating the complete [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/building-a-complete-todo-app-with-swiftui-and-supabase/">SwiftUI ToDo App Tutorial: Integrating Supabase for Backend Support</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>In this comprehensive SwiftUI tutorial for iOS developers, we&#8217;ll build a fully functional todo application using SwiftUI for iOS development and Supabase as our open-source backend service. By the end of this step-by-step guide, you&#8217;ll have created a production-ready iOS app with real-time database synchronization and complete CRUD operations.</p>



<p>We&#8217;ll start by creating the complete iOS application using the MVVM architecture pattern, and once our SwiftUI app is running smoothly, we&#8217;ll integrate the powerful Supabase backend for data persistence and real-time updates.</p>



<p>You can download the complete project here: <a href="https://github.com/AndersSoftware/SwiftUI-ToDo-app-with-Supabase-/tree/main">Go to Github</a></p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="471" height="1024" src="https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-03-at-13.35.54-471x1024.png" alt="" class="wp-image-799" srcset="https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-03-at-13.35.54-471x1024.png 471w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-03-at-13.35.54-138x300.png 138w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-03-at-13.35.54-768x1670.png 768w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-03-at-13.35.54-706x1536.png 706w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-03-at-13.35.54-942x2048.png 942w, https://softwareanders.com/wp-content/uploads/2025/06/Simulator-Screenshot-iPhone-16-Pro-2025-06-03-at-13.35.54-scaled.png 1177w" sizes="(max-width: 471px) 100vw, 471px" /></figure>



<h2 class="wp-block-heading">What You&#8217;ll Build in This SwiftUI Todo App Tutorial</h2>



<p>By following this step-by-step SwiftUI tutorial, you&#8217;ll create:</p>



<ul class="wp-block-list">
<li><strong>Complete CRUD operations</strong>&nbsp;&#8211; Create, read, update, and delete todos with SwiftUI and Supabase integration</li>



<li><strong>Real-time data synchronization</strong>&nbsp;&#8211; Instant updates across all devices using Supabase&#8217;s PostgreSQL database</li>



<li><strong>Modern iOS interface</strong>&nbsp;&#8211; Clean, intuitive SwiftUI design following Apple&#8217;s Human Interface Guidelines</li>
</ul>



<h2 class="wp-block-heading">Understanding MVVM Architecture for SwiftUI App Development</h2>



<p>Before diving into our SwiftUI todo app code, let&#8217;s understand the&nbsp;<strong>Model-View-ViewModel (MVVM)</strong>&nbsp;architectural pattern we&#8217;ll be implementing throughout this iOS development tutorial.</p>



<h3 class="wp-block-heading">What is MVVM in iOS Development?</h3>



<p>MVVM is a proven architectural pattern that organizes your SwiftUI application into three distinct, maintainable layers:</p>



<ul class="wp-block-list">
<li><strong>Model Layer</strong>: Represents your data structures and business logic (Todo entities, User models, and core app data)</li>



<li><strong>View Layer</strong>: The SwiftUI views and UI components that users interact with (TodoListView, AuthView, custom SwiftUI components)</li>



<li><strong>ViewModel Layer</strong>: The crucial bridge between Model and View, managing business logic, state management, and data flow</li>
</ul>



<h3 class="wp-block-heading">Why Use MVVM Architecture with SwiftUI?</h3>



<p>SwiftUI and MVVM create a powerful combination for iOS app development because:</p>



<ul class="wp-block-list">
<li><strong>Enhanced Reusability</strong>: ViewModels can be shared across multiple SwiftUI views, reducing code duplication</li>



<li><strong>Reactive UI Updates</strong>: ViewModels leverage&nbsp;<code>@Published</code>&nbsp;properties and ObservableObject protocol for automatic UI updates</li>



<li><strong>Clean Separation of Concerns</strong>: Business logic remains separate from SwiftUI views, creating more maintainable and readable code</li>



<li><strong>Superior Testability</strong>: Unit test ViewModels independently without UI dependencies, improving code quality and reliability</li>
</ul>



<p>This MVVM approach will make our Supabase integration cleaner and our SwiftUI todo app more scalable as we add real-time database functionality.</p>



<h3 class="wp-block-heading">MVVM Flow in Our Todo App</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>User Interaction → View → ViewModel → Service/API → Database
                    ↑                      ↓
                    ← ← ← ← ← ← ← ← ← ← ← ← </textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">User</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Interaction</span><span style="color: #D8DEE9FF"> → </span><span style="color: #D8DEE9">View</span><span style="color: #D8DEE9FF"> → </span><span style="color: #D8DEE9">ViewModel</span><span style="color: #D8DEE9FF"> → </span><span style="color: #D8DEE9">Service</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">API</span><span style="color: #D8DEE9FF"> → </span><span style="color: #D8DEE9">Database</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    ↑                      ↓</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    ← ← ← ← ← ← ← ← ← ← ← ← </span></span></code></pre></div>



<ol class="wp-block-list">
<li>User taps a button in the&nbsp;<strong>View</strong></li>



<li><strong>View</strong>&nbsp;calls a method on the&nbsp;<strong>ViewModel</strong></li>



<li><strong>ViewModel</strong>&nbsp;processes the request and calls the&nbsp;<strong>Service</strong></li>



<li><strong>Service</strong>&nbsp;makes API calls to Supabase</li>



<li>Data flows back through the chain, updating the UI reactively</li>
</ol>



<h3 class="wp-block-heading">Essential MVVM Classes for Our SwiftUI Todo App</h3>



<p>Our SwiftUI Supabase tutorial will focus on building these core architectural components:</p>



<ul class="wp-block-list">
<li><strong>TodoViewModel</strong>: Manages todo list state, handles CRUD operations, and processes real-time database updates from Supabase</li>



<li><strong>SupabaseService</strong>: Our dedicated service layer that handles all API communication with the Supabase backend</li>
</ul>



<p>Let&#8217;s start by building the data models that form the foundation of our iOS app&#8217;s architecture, then we&#8217;ll implement these ViewModels as they contain the core business logic for our SwiftUI application!</p>



<h2 class="wp-block-heading">Building Data Models for SwiftUI MVVM Architecture</h2>



<p>Before we dive into ViewModels, let&#8217;s establish the foundation &#8211; our&nbsp;<strong>Data Models</strong>. In MVVM architecture for iOS development, models are clean data structures that represent the core entities in your SwiftUI application.</p>



<h3 class="wp-block-heading">Best Practices for SwiftUI Data Models</h3>



<p>A well-designed model for iOS app development should be:</p>



<ul class="wp-block-list">
<li><strong>Simple and Clean</strong>: Contains only data properties, avoiding complex business logic that belongs in ViewModels</li>



<li><strong>Identifiable Protocol</strong>: Each model instance must be uniquely identifiable for SwiftUI&#8217;s list rendering and state management</li>



<li><strong>Equatable Protocol</strong>: Enables SwiftUI&#8217;s diffing algorithm to efficiently update the UI when underlying data changes</li>



<li><strong>Codable Ready</strong>: Structured for seamless JSON encoding/decoding with Supabase API responses</li>
</ul>



<p>These model design principles ensure optimal performance in our SwiftUI todo app and smooth integration with Supabase&#8217;s PostgreSQL database.</p>



<h3 class="wp-block-heading">Our Todo Model</h3>



<p>Right click on the Models folder and add a new swift class and write the following code:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Foundation

struct TodoItemModel: Identifiable, Equatable {
    let id = UUID()
    var title: String
    var isCompleted: Bool = false
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> TodoItemModel</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">Identifiable</span><span style="color: #D8DEE9FF">, </span><span style="color: #8FBCBB; font-weight: bold">Equatable </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> id </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">UUID</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> title: </span><span style="color: #8FBCBB">String</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isCompleted: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Let&#8217;s break this down:</p>



<ul class="wp-block-list">
<li><strong><code>struct</code></strong>: We use a struct because models are typically value types &#8211; simple data containers</li>



<li><strong><code>Identifiable</code></strong>: This protocol requires an&nbsp;<code>id</code>&nbsp;property, allowing SwiftUI to track individual todos in lists</li>



<li><strong><code>Equatable</code></strong>: This lets SwiftUI compare todos efficiently and only update the UI when something actually changes</li>



<li><strong><code>let id = UUID()</code></strong>: Each todo gets a unique identifier automatically</li>



<li><strong><code>var title</code>&nbsp;&amp;&nbsp;<code>var isCompleted</code></strong>: The actual data we care about &#8211; these can change over time</li>
</ul>



<p>This model is intentionally simple &#8211; it focuses purely on representing data, leaving all the business logic (like saving, loading, updating) to the ViewModel. This separation makes our code clean, testable, and easy to understand.</p>



<h2 class="wp-block-heading">Create the ViewModel for the todos</h2>



<p>Before we create a new class we first need to bring some order to our project &#8211; so let&#8217;s start by creating a Group/Folder called Viewmodels. Inside this folder we create our ToDo viewmodel and call it <code>TodoListViewmodel</code> &#8211; then our project structure will look like this:</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="572" height="540" src="https://softwareanders.com/wp-content/uploads/2025/05/Screenshot-2025-05-25-at-21.50.24.png" alt="" class="wp-image-729" style="width:415px;height:auto" srcset="https://softwareanders.com/wp-content/uploads/2025/05/Screenshot-2025-05-25-at-21.50.24.png 572w, https://softwareanders.com/wp-content/uploads/2025/05/Screenshot-2025-05-25-at-21.50.24-300x283.png 300w" sizes="(max-width: 572px) 100vw, 572px" /></figure>



<p>Inside our viewmodel we will create our business logic &#8211; in our case &#8211; it will be 3 functions: <code>createToDo(),</code> <code>deleteToDo()</code> and <code>toggleCompletion()</code>. In addition to the 3 functions we will also have an array that holds the todos that we will be displaying in the application.</p>



<p>Inside our <code>TodoListViewmodel</code> class we will start by an empty observable class:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Foundation

class TodoListViewmodel: ObservableObject {
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">TodoListViewmodel</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ObservableObject </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<p>Now our viewmodel conforms to&nbsp;<code>ObservableObject</code>, which is SwiftUI&#8217;s protocol for reactive data binding. </p>



<p>The next thing we are going to create is an array that conforms to our <code>ToDoModel.swift</code>, and create a init method that appends three todos to our array &#8211; an&nbsp;<code>init</code>&nbsp;method is a special function that sets up and initializes a new instance of a class or struct when it&#8217;s created.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Foundation

class TodoListViewmodel: ObservableObject {
  @Published var todos: [TodoItemModel] = []
    
      init() {
        self.todos.append(TodoItemModel(title: &#8220;Buy groceries&#8221;))
        self.todos.append(TodoItemModel(title: &#8220;Finish project&#8221;))
        self.todos.append(TodoItemModel(title: &#8220;Call mom&#8221;))
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">TodoListViewmodel</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ObservableObject </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Published</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> todos: </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">TodoItemModel</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> []</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">todos</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">append</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">TodoItemModel</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Buy groceries</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">todos</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">append</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">TodoItemModel</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Finish project</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">todos</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">append</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">TodoItemModel</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Call mom</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>When properties marked with&nbsp;<code>@Published</code>&nbsp;inside this class change, any SwiftUI views that observe this ViewModel will automatically update their UI &#8211; this is the magic that makes our todo list refresh in real-time when we add, edit, or delete items.</p>



<p>So far so good &#8211; now we need our functions to: Create and Delete.</p>



<h4 class="wp-block-heading">Create a new Todo</h4>



<p>Our <code>createTodo()</code> function is going to be pretty straightforward &#8211; the function will take a title for the todo as a parameter and then create a new TodoItemModel and then add that todo to the list of todos:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func addTodo(title: String) {
  let newTodo = TodoItemModel(title: title)
  todos.append(newTodo)
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">addTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> newTodo </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">TodoItemModel</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">  todos.</span><span style="color: #88C0D0">append</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">newTodo</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h4 class="wp-block-heading">Delete a Todo</h4>



<p>The next function we need to add is our delete function. This function will take a todo item id as a parameter and when find that id in our list of todos &#8211; when it finds this todo it will simply delete it. </p>



<p>And thanks to Swift we have already created such a function for us: removeAll(), so we will just use that:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func deleteTodo(id: UUID) {
  todos.removeAll(where: { $0.id == id })
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">deleteTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">id</span><span style="color: #D8DEE9FF">: UUID</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  todos.</span><span style="color: #88C0D0">removeAll</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">where</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$0</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> id </span><span style="color: #ECEFF4">})</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">Enable toggle on a todo</h3>



<p>Now we just need to add one last function to our viewmodel &#8211; the ability to toggle a todo.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func toggleCompletion(for todo: TodoItemModel) {
    if let index = todos.firstIndex(where: { $0.id == todo.id }) {
        todos[index].isCompleted.toggle()
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">toggleCompletion</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">todo</span><span style="color: #D8DEE9FF">: TodoItemModel</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> index </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> todos.</span><span style="color: #88C0D0">firstIndex</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">where</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$0</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> todo.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        todos</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">index</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isCompleted</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">toggle</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p><strong>What it does:</strong></p>



<ol class="wp-block-list">
<li><strong>Find the todo</strong>&nbsp;&#8211; Uses&nbsp;<code>firstIndex(where:)</code>&nbsp;to locate the todo in our array by matching its unique ID</li>



<li><strong>Toggle the status</strong>&nbsp;&#8211; If found, flips the&nbsp;<code>isCompleted</code>&nbsp;property from&nbsp;<code>true</code>&nbsp;to&nbsp;<code>false</code>&nbsp;(or vice versa)</li>



<li><strong>Update the UI</strong>&nbsp;&#8211; Since&nbsp;<code>todos</code>&nbsp;is a&nbsp;<code>@Published</code>&nbsp;property, SwiftUI automatically refreshes the interface</li>
</ol>



<p><strong>Why this approach?</strong></p>



<ul class="wp-block-list">
<li><strong>Safe</strong>&nbsp;&#8211; The&nbsp;<code>if let</code>&nbsp;ensures we only update if the todo exists</li>



<li><strong>Efficient</strong>&nbsp;&#8211; Direct array access by index is fast</li>



<li><strong>Reactive</strong>&nbsp;&#8211; Changes trigger automatic UI updates</li>
</ul>



<h3 class="wp-block-heading">Our viewmodel so far</h3>



<p>Here we have our complete viewmodel class that controls how we interact with our todos in the view. Later on we will modify it a bit and add Supabase integration.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Foundation

class TodoListViewmodel: ObservableObject {
    @Published var todos: [TodoItemModel] = [
        TodoItemModel(title: &#8220;Buy groceries&#8221;),
        TodoItemModel(title: &#8220;Finish project&#8221;),
        TodoItemModel(title: &#8220;Call mom&#8221;)
    ]
    
    func addTodo(title: String) {
        let newTodo = TodoItemModel(title: title)
        todos.append(newTodo)
    }
    
    func deleteTodo(id: UUID) {
        todos.removeAll(where: { $0.id == id })
    }
    
    func toggleCompletion(for todo: TodoItemModel) {
        if let index = todos.firstIndex(where: { $0.id == todo.id }) {
            todos[index].isCompleted.toggle()
        }
    }
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">TodoListViewmodel</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ObservableObject </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Published</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> todos: </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">TodoItemModel</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">TodoItemModel</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Buy groceries</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">TodoItemModel</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Finish project</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">TodoItemModel</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Call mom</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ]</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">addTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> newTodo </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">TodoItemModel</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        todos.</span><span style="color: #88C0D0">append</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">newTodo</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">deleteTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">id</span><span style="color: #D8DEE9FF">: UUID</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        todos.</span><span style="color: #88C0D0">removeAll</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">where</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$0</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> id </span><span style="color: #ECEFF4">})</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">toggleCompletion</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">todo</span><span style="color: #D8DEE9FF">: TodoItemModel</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> index </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> todos.</span><span style="color: #88C0D0">firstIndex</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">where</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$0</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> todo.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            todos</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">index</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isCompleted</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">toggle</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p></p>



<h2 class="wp-block-heading">Build the view in SwiftUI</h2>



<p>This is where the application comes together of this tutorial &#8211; we are going to create our view and use our viewmodel, &#8211; time to build the complete application!</p>



<p>When we created our project in the beginning xCode gave us some code and classes to start with, one of those is called ContentView.swift. To keep things simple we will just use that SwiftUI class.</p>



<p>To make this part a bit easier to follow I have put it in three sections and at the end the complete view is.</p>



<h3 class="wp-block-heading">Connecting View to ViewModel</h3>



<p>Now let&#8217;s look at how our SwiftUI view connects to the ViewModel:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>@StateObject private var todoStore = TodoListViewmodel()
@State private var newTodoTitle = &#8220;&#8221;</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">StateObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> todoStore </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">TodoListViewmodel</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> newTodoTitle </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span></code></pre></div>



<p><strong><code>@StateObject</code></strong>&nbsp;creates and owns our ViewModel &#8211; this tells SwiftUI to keep the&nbsp;<code>todoStore</code>&nbsp;alive for the entire lifetime of this view. When any&nbsp;<code>@Published</code>&nbsp;property in our ViewModel changes, this view will automatically refresh.</p>



<p><strong><code>@State</code></strong>&nbsp;manages local UI state for our text field. This is perfect for temporary data that only this view cares about, like what the user is currently typing.</p>



<h3 class="wp-block-heading">The UI Breakdown</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>var body: some View {
    VStack {
        // UI components here
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// UI components here</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The&nbsp;<strong><code>VStack</code></strong>&nbsp;is our main container that arranges all UI elements vertically from top to bottom. This gives us a clean, organized layout structure.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>Text(&#8220;TODO LIST&#8221;)
    .font(Font.title)
    .bold()</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">TODO LIST</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Font.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span></code></pre></div>



<p>A simple header with title styling &#8211; nothing fancy here, just clean UI presentation.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>List {
    ForEach(todoStore.todos) { todo in
        TodoListItemView(todo: todo, toggleCompletion: { todo in
            todoStore.toggleCompletion(for: todo)
        })
    }
    .onDelete(perform: deleteTodos)
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">List</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ForEach</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">todoStore.</span><span style="color: #D8DEE9">todos</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> todo </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">TodoListItemView</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">todo</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> todo, </span><span style="color: #88C0D0">toggleCompletion</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> todo </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">            todoStore.</span><span style="color: #88C0D0">toggleCompletion</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> todo</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">})</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">onDelete</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">perform</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> deleteTodos</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The&nbsp;<strong><code>ForEach</code></strong>&nbsp;loops through our todos from the ViewModel. Each time&nbsp;<code>todoStore.todos</code>&nbsp;changes, SwiftUI automatically rebuilds this list. </p>



<p>We&#8217;re passing each todo to a separate&nbsp;<strong><code>TodoListItemView</code></strong>&nbsp;component (keeping our code modular), along with a&nbsp;<strong>closure</strong>&nbsp;that handles toggle completion. </p>



<p>The&nbsp;<strong><code>.onDelete</code></strong>&nbsp;modifier handles swipe-to-delete functionality.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>HStack {
    TextField(&#8220;Add new todo&#8221;, text: $newTodoTitle)
        .textFieldStyle(RoundedBorderTextFieldStyle())
    
    Button(action: {
        if !newTodoTitle.isEmpty {
            todoStore.addTodo(title: newTodoTitle)
            newTodoTitle = &#8220;&#8221;
        }
    }) {
        Image(systemName: &#8220;plus.circle.fill&#8221;)
            .font(.title)
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">HStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Add new todo</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $newTodoTitle</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">action</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF">newTodoTitle.isEmpty </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            todoStore.</span><span style="color: #88C0D0">addTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> newTodoTitle</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            newTodoTitle </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">plus.circle.fill</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The&nbsp;<strong><code>HStack</code></strong>&nbsp;creates a horizontal input area. The&nbsp;<strong><code>TextField</code></strong>&nbsp;uses&nbsp;<strong>two-way binding</strong>&nbsp;(<code>$newTodoTitle</code>) so typing updates our state automatically. </p>



<p>The&nbsp;<strong><code>Button</code></strong>&nbsp;validates the input isn&#8217;t empty, calls our ViewModel method, then clears the text field.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>private func deleteTodos(offsets: IndexSet) {
    for index in offsets {
        todoStore.deleteTodo(id: todoStore.todos[index].id)
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">deleteTodos</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">offsets</span><span style="color: #D8DEE9FF">: IndexSet</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> index </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> offsets </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        todoStore.</span><span style="color: #88C0D0">deleteTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> todoStore.</span><span style="color: #D8DEE9">todos</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">index</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>This&nbsp;<strong>helper function</strong>&nbsp;handles the swipe-to-delete action.&nbsp;<strong><code>IndexSet</code></strong>&nbsp;contains the positions of items to delete, so we loop through and call our ViewModel&#8217;s delete method for each one.</p>



<h3 class="wp-block-heading">The complete SwiftUI view</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import SwiftUI

struct ContentView: View {
    @StateObject private var todoStore = TodoListViewmodel()
    @State private var newTodoTitle = &#8220;&#8221;
    
    var body: some View {
            VStack {
                Text(&#8220;TODO LIST&#8221;)
                    .font(Font.title)
                    .bold()
                
                List {
                    ForEach(todoStore.todos) { todo in
                        TodoListItemView(todo: todo, toggleCompletion: { todo in
                            todoStore.toggleCompletion(for: todo)
                        })
                    }
                    .onDelete(perform: deleteTodos)

                }
                
                HStack {
                    TextField(&#8220;Add new todo&#8221;, text: $newTodoTitle)
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                    
                    Button(action: {
                        if !newTodoTitle.isEmpty {
                            todoStore.addTodo(title: newTodoTitle)
                            newTodoTitle = &#8220;&#8221;
                        }
                    }) {
                        Image(systemName: &#8220;plus.circle.fill&#8221;)
                            .font(.title)
                    }
                }
                .padding()
            }

    }
    
    private func deleteTodos(offsets: IndexSet) {
        for index in offsets {
            todoStore.deleteTodo(id: todoStore.todos[index].id)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ContentView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">StateObject</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> todoStore </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">TodoListViewmodel</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> newTodoTitle </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">TODO LIST</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Font.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">List</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">ForEach</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">todoStore.</span><span style="color: #D8DEE9">todos</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> todo </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">TodoListItemView</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">todo</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> todo, </span><span style="color: #88C0D0">toggleCompletion</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> todo </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            todoStore.</span><span style="color: #88C0D0">toggleCompletion</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> todo</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">})</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">onDelete</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">perform</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> deleteTodos</span><span style="color: #ECEFF4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">HStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Add new todo</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $newTodoTitle</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        .</span><span style="color: #88C0D0">textFieldStyle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedBorderTextFieldStyle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">action</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF">newTodoTitle.isEmpty </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            todoStore.</span><span style="color: #88C0D0">addTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> newTodoTitle</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            newTodoTitle </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">plus.circle.fill</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">deleteTodos</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">offsets</span><span style="color: #D8DEE9FF">: IndexSet</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> index </span><span style="color: #81A1C1">in</span><span style="color: #D8DEE9FF"> offsets </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            todoStore.</span><span style="color: #88C0D0">deleteTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> todoStore.</span><span style="color: #D8DEE9">todos</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">index</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ContentView_Previews</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">PreviewProvider </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> previews: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ContentView</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p></p>



<h2 class="wp-block-heading">Taking It Further: Adding Supabase Backend</h2>



<p>Great! We now have a fully functional todo app with proper MVVM architecture. But what if we want to sync our todos across devices, share them with friends, or simply have them backed up in the cloud?</p>



<p>This is where&nbsp;<strong>Supabase</strong>&nbsp;comes in &#8211; a powerful backend-as-a-service that gives us a PostgreSQL database, real-time subscriptions, authentication, and more, all with just a few lines of code.</p>



<p>In this section, we&#8217;ll evolve our local todo app into a cloud-powered application that can sync data across devices in real-time.</p>



<h3 class="wp-block-heading">What We&#8217;ll Add</h3>



<ul class="wp-block-list">
<li><strong>Cloud Storage</strong>: Todos saved to a PostgreSQL database</li>



<li><strong>Real-time Sync</strong>: Changes appear instantly across all devices</li>



<li><strong>Offline Support</strong>: App works even without internet connection</li>
</ul>



<h3 class="wp-block-heading">Setting Up Supabase</h3>



<h4 class="wp-block-heading">1. Create a Supabase Project</h4>



<p>First, head over to&nbsp;<a href="https://supabase.com/">supabase.com</a>&nbsp;and create a free account. Once logged in:</p>



<ol class="wp-block-list">
<li>Click &#8220;New Project&#8221;</li>



<li>Choose your organization</li>



<li>Enter a project name (e.g., &#8220;SwiftUI Todo App&#8221;)</li>



<li>Create a secure database password</li>



<li>Select a region close to your users</li>



<li>Click &#8220;Create new project&#8221;</li>
</ol>



<p>Your project will take a minute to set up. Once ready, you&#8217;ll need two key pieces of information:</p>



<ul class="wp-block-list">
<li><strong>API URL</strong>: Found in <code>Project Settings -&gt; Data API </code></li>



<li><strong>API Key</strong>: Also in <code>Project Settings -&gt; <code>API</code></code> <code>KEYS </code></li>
</ul>



<h4 class="wp-block-heading">2. Database Setup</h4>



<p>In your Supabase dashboard you can create a database table in two ways: By clicking or by writing SQL. I will quickly show you the two ways and then in a later tutorial we will dive deeper in Supabase.</p>



<h5 class="wp-block-heading">Create table by clicking</h5>



<p>The first method is probably the most straightforward if you&#8217;re completely new. In the left menu you will find &#8220;Table editor&#8221;, click that. Now on the middle of the screen you will find a button called &#8220;Create a table. Now we will just fill out how our table should look like:</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" width="703" height="1024" src="https://softwareanders.com/wp-content/uploads/2025/05/Screenshot-2025-05-30-at-14.48.01-703x1024.png" alt="" class="wp-image-751" style="width:487px;height:auto" srcset="https://softwareanders.com/wp-content/uploads/2025/05/Screenshot-2025-05-30-at-14.48.01-703x1024.png 703w, https://softwareanders.com/wp-content/uploads/2025/05/Screenshot-2025-05-30-at-14.48.01-206x300.png 206w, https://softwareanders.com/wp-content/uploads/2025/05/Screenshot-2025-05-30-at-14.48.01-768x1119.png 768w, https://softwareanders.com/wp-content/uploads/2025/05/Screenshot-2025-05-30-at-14.48.01-1054x1536.png 1054w, https://softwareanders.com/wp-content/uploads/2025/05/Screenshot-2025-05-30-at-14.48.01.png 1352w" sizes="(max-width: 703px) 100vw, 703px" /></figure>



<h5 class="wp-block-heading">Create table using SQL</h5>



<p>You can also create the table by using SQL and to do that you just go to the SQL editor in the left menu &#8211; it&#8217;s right under the &#8220;Table editor&#8221;. When you are there you simply copy and paste the following SQL into the SQL editor and click run:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>CREATE TABLE todos (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  title TEXT NOT NULL,
  is_completed BOOLEAN DEFAULT FALSE
);</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">CREATE</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">TABLE</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">todos</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">  id UUID </span><span style="color: #81A1C1">DEFAULT</span><span style="color: #D8DEE9FF"> gen_random_uuid</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">PRIMARY KEY</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  title </span><span style="color: #81A1C1">TEXT</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">NOT NULL</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  is_completed </span><span style="color: #81A1C1">BOOLEAN</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">DEFAULT</span><span style="color: #D8DEE9FF"> FALSE</span></span>
<span class="line"><span style="color: #D8DEE9FF">);</span></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">Adding Supabase to Your iOS Project</h3>



<p>Now we have created our database and now it&#8217;s time to add Supabase to our project. Luckily this is pretty straightforward and Supabase have created an excellent package we will use.</p>



<p>In Xcode, go to&nbsp;<strong>File → Add Package Dependencies</strong>&nbsp;and add:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>https://github.com/supabase/supabase-swift</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">https</span><span style="color: #81A1C1">:</span><span style="color: #616E88">//github.com/supabase/supabase-swift</span></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">Prepare TodoItemModel for Supabase</h3>



<p>The first thing we need to do is to make sure our model is ready to work with data from Supabase &#8211; to do that we will add <code>Codable</code> to pass from JSON to model, CodingKeys to convert names and then two init one for local and one for Supabase.</p>



<p>The complete model will look like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Foundation

struct TodoItemModel: Identifiable, Equatable, Codable {
    var id = UUID()
    var title: String
    var isCompleted: Bool = false
    
    enum CodingKeys: String, CodingKey {
            case id
            case title
            case isCompleted = &#8220;is_completed&#8221;
        }
        
    // For creating new todos locally
    init(title: String, isCompleted: Bool = false) {
        self.id = UUID()
        self.title = title
        self.isCompleted = isCompleted
    }
    
    // For todos coming from Supabase
    init(id: UUID, title: String, isCompleted: Bool) {
        self.id = id
        self.title = title
        self.isCompleted = isCompleted
    }
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> TodoItemModel</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">Identifiable</span><span style="color: #D8DEE9FF">, </span><span style="color: #8FBCBB; font-weight: bold">Equatable</span><span style="color: #D8DEE9FF">, </span><span style="color: #8FBCBB; font-weight: bold">Codable </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> id </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">UUID</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> title: </span><span style="color: #8FBCBB">String</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isCompleted: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">enum</span><span style="color: #D8DEE9FF"> CodingKeys</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #8FBCBB; font-weight: bold">CodingKey </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">id</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">title</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">case</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">isCompleted</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">is_completed</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// For creating new todos locally</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isCompleted</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">UUID</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> title</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isCompleted</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> isCompleted</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// For todos coming from Supabase</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">id</span><span style="color: #D8DEE9FF">: UUID, </span><span style="color: #88C0D0">title</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isCompleted</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">Bool</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> id</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> title</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isCompleted</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> isCompleted</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">Create Supabase service</h3>



<p>Now to accurately work with the Supabase database we need to create a Supabase service, we do that by simply creating a new Swift class and call it <code>SupabaseService.swift</code>.</p>



<p>Inside our <code>SupabaseService</code> class we will create a init method that creates our Supabase client. Then we will create one function to fetch all the todos, one function to create a todo, one function to toggle a todo and one function to delete a todo.</p>



<p>Remember add your own Supabase url and key.</p>



<p>You will find the API URL at: <code>Project Settings -&gt; Data API </code></p>



<p>You will find the API KEY at: <code>Project Settings -&gt; API Keys </code></p>



<p>They will look something like this:</p>



<p><code>Supabase url: https://saarqrycgjxqlmmefbsr.supabase.co</code></p>



<p><code>Supabase API key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNhYXJxcnljZ2p4cWxtbWVmYnNyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDg2MDc3NjQsImV4cCI6MjA2NDE4Mzc2NH0.BNBvtwmgGW6at1_WnFhnZ6FLu8KPFEzr1UQ2C8Y27OM</code></p>



<h4 class="wp-block-heading">Supabase init</h4>



<p>As with our viewmodel this service will also have a Init function &#8211; inside the init we will create a new Supabase client with URL and API key:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>
class SupabaseService: ObservableObject {
    static let shared = SupabaseService()
    
    let client: SupabaseClient
    
    private init() {
        // Replace these with your actual Supabase URL and API key
        let supabaseURL = URL(string: &#8220;YOUR_SUPABASE_PROJECT_URL&#8221;)!
        let supabaseKey = &#8220;YOUR_SUPABASE_ANON_KEY&#8221;
        
        self.client = SupabaseClient(
            supabaseURL: supabaseURL,
            supabaseKey: supabaseKey
        )
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">SupabaseService</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ObservableObject </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> shared </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">SupabaseService</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> client: SupabaseClient</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Replace these with your actual Supabase URL and API key</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> supabaseURL </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">URL</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">string</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">YOUR_SUPABASE_PROJECT_URL</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">!</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> supabaseKey </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">YOUR_SUPABASE_ANON_KEY</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">client</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">SupabaseClient</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">supabaseURL</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> supabaseURL,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">supabaseKey</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> supabaseKey</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h4 class="wp-block-heading">Fetch the todos</h4>



<p>The next function we are going to create is the function that fetches all the todos from Supabase:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func fetchTodos() async throws -> [TodoItemModel] {
    let response: [TodoItemModel] = try await client
        .from(&#8220;todos&#8221;)
        .select()
        .execute()
        .value
        
    return response
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetchTodos</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">TodoItemModel</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> response: </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">TodoItemModel</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> client</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">from</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">todos</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">select</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .value</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> response</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p><strong>Breaking it down:</strong></p>



<ul class="wp-block-list">
<li><strong><code>async throws</code></strong>&nbsp;&#8211; This function waits for the database and can handle errors</li>



<li><strong><code>.from("todos")</code></strong>&nbsp;&#8211; Points to your &#8220;todos&#8221; table in the database</li>



<li><strong><code>.select()</code></strong>&nbsp;&#8211; Gets all the todo data (like SQL SELECT *)</li>



<li><strong><code>.execute()</code></strong>&nbsp;&#8211; Actually runs the database query</li>



<li><strong><code>.value</code></strong>&nbsp;&#8211; Extracts the JSON data and converts it to&nbsp;<code>[TodoItemModel]</code></li>
</ul>



<p><strong>In simple terms:</strong>&nbsp;This function asks the database &#8220;give me all my todos&#8221; and automatically converts the response into an array of&nbsp;<code>TodoItemModel</code>&nbsp;objects that your SwiftUI app can use.</p>



<h4 class="wp-block-heading">Create a todo</h4>



<p>The next function we are going to create is the function that creates a new todo:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func createTodo(_ todo: TodoItemModel) async throws -> TodoItemModel {
    let todoData = [
        &#8220;title&#8221;: todo.title,
        &#8220;is_completed&#8221;: todo.isCompleted
    ] as [String: Any]
        
    let response: TodoItemModel = try await client
        .from(&#8220;todos&#8221;)
        .insert(todoData)
        .select()
        .single()
        .execute()
        .value
        
    return response
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">createTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">todo</span><span style="color: #D8DEE9FF">: TodoItemModel</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> TodoItemModel </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> todoData </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">title</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> todo.</span><span style="color: #D8DEE9">title</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">is_completed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> todo.</span><span style="color: #D8DEE9">isCompleted</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ] </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #88C0D0">String</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Any</span><span style="color: #ECEFF4">]</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> response: TodoItemModel </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> client</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">from</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">todos</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">insert</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">todoData</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">select</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">single</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .value</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> response</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p><strong>Breaking it down:</strong></p>



<ul class="wp-block-list">
<li><strong><code>todoData</code></strong>&nbsp;&#8211; Converts your todo into a format the database understands</li>



<li><strong><code>.insert(todoData)</code></strong>&nbsp;&#8211; Adds the new todo to the database</li>



<li><strong><code>.select()</code></strong>&nbsp;&#8211; Asks the database to return the saved todo</li>



<li><strong><code>.single()</code></strong>&nbsp;&#8211; Expects exactly one todo back (the one we just created)</li>



<li><strong>Returns the saved todo</strong>&nbsp;&#8211; Complete with database-generated ID and timestamps</li>
</ul>



<p><strong>In simple terms:</strong>&nbsp;This function tells the database &#8220;save this new todo for me&#8221; and gives you back the official saved version &#8211; like getting a receipt after making a purchase!</p>



<h4 class="wp-block-heading">Toggle a todo</h4>



<p>The next function we are making is the function that toggles a todo isCompleted property.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func toggleTodo(_ todo: TodoItemModel) async throws -> TodoItemModel {
    let updateData = [
        &#8220;is_completed&#8221;: AnyJSON.bool(todo.isCompleted)
    ]
        
    let response: TodoItemModel = try await client
        .from(&#8220;todos&#8221;)
        .update(updateData)
        .eq(&#8220;id&#8221;, value: todo.id)
        .select()
        .single()
        .execute()
        .value
        
    return response
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">toggleTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">todo</span><span style="color: #D8DEE9FF">: TodoItemModel</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> TodoItemModel </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> updateData </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">is_completed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> AnyJSON.bool</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">todo.</span><span style="color: #D8DEE9">isCompleted</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ]</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> response: TodoItemModel </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> client</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">from</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">todos</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">update</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">updateData</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">eq</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">id</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> todo.</span><span style="color: #D8DEE9">id</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">select</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">single</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .value</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> response</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p><strong>Breaking it down:</strong></p>



<ul class="wp-block-list">
<li><strong><code>updateData</code></strong>&nbsp;&#8211; Contains only the field we want to change (<code>is_completed</code>)</li>



<li><strong><code>.update(updateData)</code></strong>&nbsp;&#8211; Tells the database what to change</li>



<li><strong><code>.eq("id", value: todo.id)</code></strong>&nbsp;&#8211; Finds the exact todo using its unique ID</li>



<li><strong><code>.select().single()</code></strong>&nbsp;&#8211; Gets back the updated todo</li>



<li><strong><code>AnyJSON.bool()</code></strong>&nbsp;&#8211; Converts the boolean to Supabase&#8217;s expected format</li>
</ul>



<p><strong>In simple terms:</strong>&nbsp;This function tells the database &#8220;find this specific todo and flip its completion status&#8221; &#8211; like checking/unchecking a box, but in the cloud!</p>



<h4 class="wp-block-heading">Delete a todo</h4>



<p></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>func deleteTodo(id: UUID) async throws {
    try await client
        .from(&#8220;todos&#8221;)
        .delete()
        .eq(&#8220;id&#8221;, value: id)
        .execute()
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">deleteTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">id</span><span style="color: #D8DEE9FF">: UUID</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> client</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">from</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">todos</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">delete</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">eq</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">id</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> id</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p><strong>Breaking it down:</strong></p>



<ul class="wp-block-list">
<li><strong><code>.delete()</code></strong>&nbsp;&#8211; Tells the database we want to remove something</li>



<li><strong><code>.eq("id", value: id)</code></strong>&nbsp;&#8211; Finds the exact todo using its unique ID</li>



<li><strong><code>.execute()</code></strong>&nbsp;&#8211; Actually performs the deletion</li>



<li><strong>No return value</strong>&nbsp;&#8211; Once deleted, it&#8217;s gone forever!</li>
</ul>



<p><strong>In simple terms:</strong>&nbsp;This function tells the database &#8220;find this specific todo and delete it completely&#8221; &#8211; like throwing a piece of paper in the trash, but in the cloud!</p>



<p><strong>Note:</strong>&nbsp;This is permanent &#8211; once executed, the todo is gone from your database forever.</p>



<h4 class="wp-block-heading">The complete SupabaseService</h4>



<p>The final Supabase service will look like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Foundation
import Supabase

class SupabaseService: ObservableObject {
    static let shared = SupabaseService()
    let client: SupabaseClient
    
    private init() {
        // Replace these with your actual Supabase URL and API key
        let supabaseURL = URL(string: &#8220;YOUR_SUPABASE_PROJECT_URL&#8221;)!
        let supabaseKey = &#8220;YOUR_SUPABASE_ANON_KEY&#8221;
        
        self.client = SupabaseClient(
            supabaseURL: supabaseURL,
            supabaseKey: supabaseKey
        )
    }
        
    func fetchTodos() async throws -> [TodoItemModel] {
        let response: [TodoItemModel] = try await client
            .from(&#8220;todos&#8221;)
            .select()
            .execute()
            .value
        return response
    }
    
    func createTodo(_ todo: TodoItemModel) async throws -> TodoItemModel {
        let todoData = [
            &#8220;title&#8221;: AnyJSON.string(todo.title),
            &#8220;is_completed&#8221;: AnyJSON.bool(todo.isCompleted)
        ]
        
        let response: TodoItemModel = try await client
            .from(&#8220;todos&#8221;)
            .insert(todoData)
            .select()
            .single()
            .execute()
            .value
        return response
    }
    
    func toggleTodo(_ todo: TodoItemModel) async throws -> TodoItemModel {
        let updateData = [
            &#8220;is_completed&#8221;: AnyJSON.bool(todo.isCompleted)
        ]
        
        let response: TodoItemModel = try await client
            .from(&#8220;todos&#8221;)
            .update(updateData)
            .eq(&#8220;id&#8221;, value: todo.id)
            .select()
            .single()
            .execute()
            .value
        return response
    }
    
    func deleteTodo(id: UUID) async throws {
        try await client
            .from(&#8220;todos&#8221;)
            .delete()
            .eq(&#8220;id&#8221;, value: id)
            .execute()
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Supabase</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">SupabaseService</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ObservableObject </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">static</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> shared </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">SupabaseService</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> client: SupabaseClient</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">        </span><span style="color: #616E88">// Replace these with your actual Supabase URL and API key</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> supabaseURL </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">URL</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">string</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">YOUR_SUPABASE_PROJECT_URL</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">!</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> supabaseKey </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">YOUR_SUPABASE_ANON_KEY</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">client</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">SupabaseClient</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">supabaseURL</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> supabaseURL,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">supabaseKey</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> supabaseKey</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetchTodos</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">TodoItemModel</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> response: </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">TodoItemModel</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> client</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">from</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">todos</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">select</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .value</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> response</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">createTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">todo</span><span style="color: #D8DEE9FF">: TodoItemModel</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> TodoItemModel </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> todoData </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">title</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> AnyJSON.</span><span style="color: #88C0D0">string</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">todo.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">is_completed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> AnyJSON.bool</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">todo.</span><span style="color: #D8DEE9">isCompleted</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        ]</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> response: TodoItemModel </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> client</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">from</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">todos</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">insert</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">todoData</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">select</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">single</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .value</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> response</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">toggleTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">_</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">todo</span><span style="color: #D8DEE9FF">: TodoItemModel</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> TodoItemModel </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> updateData </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">is_completed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> AnyJSON.bool</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">todo.</span><span style="color: #D8DEE9">isCompleted</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        ]</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> response: TodoItemModel </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> client</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">from</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">todos</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">update</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">updateData</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">eq</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">id</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> todo.</span><span style="color: #D8DEE9">id</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">select</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">single</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .value</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> response</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">deleteTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">id</span><span style="color: #D8DEE9FF">: UUID</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">throws</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> client</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">from</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">todos</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">delete</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">eq</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">id</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> id</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">execute</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">Update the viewmodel to use Supabase service</h3>



<p>Now we are at the end of this tutorial and the last thing we need to do is to add the supabaseService to our viewmodel &#8211; and it should work.</p>



<p>The first thing we are going to do is to add our Supabase service to our viewmodel and then create a new function called: <code>fetchTodos()</code> &#8211; this function is going to fetch all the todos from Supabase and pass them to our Todos array. We will replace the code in init with a call to the <code>fetchTodos():</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>  init() {
        fetchTodos()
    }
    
    func fetchTodos() {
        
        Task {
            do {
                todos = try await supabaseService.fetchTodos()
            } catch {
                print(&#8220;Error loading todos: \(error)&#8221;)
            }
        }
    }</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">fetchTodos</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetchTodos</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                todos </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabaseService.</span><span style="color: #88C0D0">fetchTodos</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Error loading todos: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<p>The next thing we are going to do is to replace the code in the other functions with code that uses our Supabase service &#8211; we will do the following:</p>



<p><strong><code>Task</code></strong>&nbsp;&#8211; Creates a new async context to handle cloud operations without blocking the UI</p>



<p><strong><code>do { }</code></strong>&nbsp;&#8211; Attempts the cloud operation that might fail</p>



<p><strong><code>catch { }</code></strong>&nbsp;&#8211; Handles network errors, server issues, or other failures gracefully</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th><strong>Old (Local)</strong></th><th><strong>New (Cloud)</strong></th></tr><tr><td>Instant updates</td><td>Waits for cloud confirmation</td></tr><tr><td>Data lost on app restart</td><td>Data persists forever</td></tr><tr><td>Works offline only</td><td>Syncs across devices</td></tr><tr><td>Simple direct changes</td><td>Async operations with error handling</td></tr></tbody></table></figure>



<p>And here we have the complete code:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import Foundation

class TodoListViewmodel: ObservableObject {
    @Published var todos: [TodoItemModel] = []
    private let supabaseService = SupabaseService.shared

    init() {
        fetchTodos()
    }
    
    func fetchTodos() {
        
        Task {
            do {
                todos = try await supabaseService.fetchTodos()
            } catch {
                print(&#8220;Error loading todos: \(error)&#8221;)
            }
        }
    }
    
    func toggleCompletion(for todo: TodoItemModel) {
        var updatedTodo = todo
        updatedTodo.isCompleted.toggle()
        
        Task {
            do {
                let serverTodo = try await supabaseService.updateTodo(updatedTodo)
                
                if let index = todos.firstIndex(where: { $0.id == todo.id }) {
                    todos[index] = serverTodo
                }
            } catch {
                print(&#8220;Failed to update todo: \(error.localizedDescription)&#8221;)
            }
        }
    }
    
    func addTodo(title: String) {
          let newTodo = TodoItemModel(title: title)
          
          Task {
              do {
                  let createdTodo = try await supabaseService.createTodo(newTodo)
                  todos.insert(createdTodo, at: 0)
              } catch {
                  print(&#8220;Failed to add todo: \(error.localizedDescription)&#8221;)
              }
          }
      }
    
    func deleteTodo(id: UUID) {
        Task {
            do {
                try await supabaseService.deleteTodo(id: id)
                
                todos.removeAll { $0.id == id }
            } catch {
                print(&#8220;Failed to delete todo: \(error.localizedDescription)&#8221;)
            }
        }
    }
}</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> Foundation</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">TodoListViewmodel</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">ObservableObject </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Published</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> todos: </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">TodoItemModel</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> []</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> supabaseService </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> SupabaseService.</span><span style="color: #D8DEE9">shared</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">init</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">fetchTodos</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetchTodos</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                todos </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabaseService.</span><span style="color: #88C0D0">fetchTodos</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Error loading todos: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">toggleCompletion</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">for</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">todo</span><span style="color: #D8DEE9FF">: TodoItemModel</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> updatedTodo </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> todo</span></span>
<span class="line"><span style="color: #D8DEE9FF">        updatedTodo.</span><span style="color: #D8DEE9">isCompleted</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">toggle</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> serverTodo </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabaseService.</span><span style="color: #88C0D0">updateTodo</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">updatedTodo</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> index </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> todos.</span><span style="color: #88C0D0">firstIndex</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">where</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$0</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> todo.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    todos</span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9FF">index</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> serverTodo</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Failed to update todo: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error.</span><span style="color: #D8DEE9">localizedDescription</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">addTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> newTodo </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">TodoItemModel</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">title</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  </span><span style="color: #81A1C1">let</span><span style="color: #D8DEE9FF"> createdTodo </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabaseService.</span><span style="color: #88C0D0">createTodo</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">newTodo</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  todos.</span><span style="color: #88C0D0">insert</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">createdTodo, </span><span style="color: #88C0D0">at</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Failed to add todo: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error.</span><span style="color: #D8DEE9">localizedDescription</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">deleteTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">id</span><span style="color: #D8DEE9FF">: UUID</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Task</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">do</span><span style="color: #ECEFF4"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> supabaseService.</span><span style="color: #88C0D0">deleteTodo</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> id</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                todos.</span><span style="color: #88C0D0">removeAll</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">$0</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">id</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> id </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Failed to delete todo: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">error.</span><span style="color: #D8DEE9">localizedDescription</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h2 class="wp-block-heading">Your SwiftUI Todo App with Supabase is Complete</h2>



<p>Congratulations! You&#8217;ve successfully built a fully functional SwiftUI todo application with Supabase backend integration. Throughout this comprehensive iOS development tutorial, you&#8217;ve learned to implement:</p>



<ul class="wp-block-list">
<li><strong>Modern MVVM Architecture</strong>&nbsp;&#8211; Clean separation of concerns with scalable ViewModels and data models</li>



<li><strong>Complete CRUD Operations</strong>&nbsp;&#8211; Create, read, update, and delete todos with real-time database synchronization</li>



<li><strong>Supabase Integration</strong>&nbsp;&#8211; Leveraging PostgreSQL database, authentication, and real-time subscriptions</li>



<li><strong>Production-Ready iOS App</strong>&nbsp;&#8211; Offline capability, error handling, and optimized SwiftUI performance</li>
</ul>



<p>You can download the complete project here: <a href="https://github.com/AndersSoftware/SwiftUI-ToDo-app-with-Supabase-/tree/main">Go to Github</a></p>



<p></p>
<p>The post <a href="https://softwareanders.com/building-a-complete-todo-app-with-swiftui-and-supabase/">SwiftUI ToDo App Tutorial: Integrating Supabase for Backend Support</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/building-a-complete-todo-app-with-swiftui-and-supabase/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI ViewBuilder: Organize your views</title>
		<link>https://softwareanders.com/swiftui-viewbuilder-organize-your-views/</link>
					<comments>https://softwareanders.com/swiftui-viewbuilder-organize-your-views/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Thu, 27 Jun 2024 14:02:40 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=682</guid>

					<description><![CDATA[<p>ViewBuilders&#160;in SwiftUI are an excellent way to keep your code clean and organized. They make your code easier to read and maintain by allowing you to structure your views more clearly. This simplifies the process of building complex user interfaces, ensuring that your code remains elegant and efficient. Embracing&#160;ViewBuilders&#160;enhances both the readability and manageability of [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/swiftui-viewbuilder-organize-your-views/">SwiftUI ViewBuilder: Organize your views</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p><code>ViewBuilders</code>&nbsp;in SwiftUI are an excellent way to keep your code clean and organized. They make your code easier to read and maintain by allowing you to structure your views more clearly. This simplifies the process of building complex user interfaces, ensuring that your code remains elegant and efficient. Embracing&nbsp;<code>ViewBuilders</code>&nbsp;enhances both the readability and manageability of your SwiftUI projects.</p>



<h2 class="wp-block-heading">What is a ViewBuilder in&nbsp;SwiftUI?</h2>



<p>ViewBuilder is just an attribute in SwiftUI you can use to help organize your views.</p>



<p>ViewBuilder is a great tool to organize your views. When developing an application it’s quite easy to create large views that do a lot different stuff. The problem is that the view can get so large that it’s hard to read, making the development time even longer. This is where using ViewBuilder is amazing — because it helps you organize your SwiftUI views.</p>



<h2 class="wp-block-heading">ViewBuilder example</h2>



<p>In the following example, I have created the most basic example of how you can use ViewBuilders.</p>



<p>We will create a ViewBuilder that simply shows a text element:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ViewBuilderView: View {
    var body: some View {
        viewBuilderExample()
    }
    
    @ViewBuilder
    func viewBuilderExample() -&gt; some View {
        Text(&quot;Hello from ViewBuilder&quot;)
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ViewBuilderView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">viewBuilderExample</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">ViewBuilder</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">viewBuilderExample</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Hello from ViewBuilder</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="594" height="96" src="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-21.04.57.png" alt="" class="wp-image-683" srcset="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-21.04.57.png 594w, https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-21.04.57-300x48.png 300w" sizes="(max-width: 594px) 100vw, 594px" /></figure>



<p></p>



<h2 class="wp-block-heading">Split up view in ViewBuilders</h2>



<p>As stated ViewBuilder can help you organize your views. To show a bit more complex example (nothing too crazy), I have made a Form with some different settings you can edit and a section that displays a list. In the first part of this example we can see how it looks without using ViewBuilders:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ViewBuilderView: View {
    @State var isToggleOn: Bool = false
    @State var listToShow: [String] = [&quot;Kade Turner&quot;, &quot;Ariella Briggs&quot;, &quot;Kensley Carroll&quot;]
    @State var fullNameTxt: String = &quot;&quot;
    @State var age: Int = 18
    
    var body: some View {
        VStack {
            Text(&quot;VIEW BUILDER EXAMPLE&quot;)
                .font(.title)
                .bold()
            
            Form {
                Section(&quot;BASIC&quot;) {
                    TextField(&quot;Full name&quot;, text: $fullNameTxt)
                    
                    Toggle(isOn: $isToggleOn, label: {
                        Text(&quot;Dark mode&quot;)
                    })
                    
                    Stepper(&quot;Age: \(age)&quot;, value: $age)
                }
                
                Section(&quot;USERS&quot;) {
                    ForEach(listToShow, id: \.self) { item in
                        Text(item)
                    }
                }
                
            }
        }

    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ViewBuilderView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isToggleOn: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> listToShow: </span><span style="color: #ECEFF4">[</span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Kade Turner</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Ariella Briggs</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Kensley Carroll</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">]</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> fullNameTxt: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> age: </span><span style="color: #8FBCBB">Int</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">18</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">VIEW BUILDER EXAMPLE</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Form</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Section</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">BASIC</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Full name</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $fullNameTxt</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Toggle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">isOn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $isToggleOn, </span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Dark mode</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">})</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">Stepper</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Age: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">age</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $age</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Section</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">USERS</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">ForEach</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">listToShow, </span><span style="color: #88C0D0">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> \.</span><span style="color: #81A1C1">self</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> item </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">item</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<p>Now it’s time to split the view into ViewBuilders. I think it’s a good idea to take every section and create a ViewBuilder. Because there can be many sections and therefore can get quite complex to read, and it feels like a natural place to split the view:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ViewBuilderView: View {
    @State var isToggleOn: Bool = false
    @State var listToShow: [String] = [&quot;Kade Turner&quot;, &quot;Ariella Briggs&quot;, &quot;Kensley Carroll&quot;]
    @State var fullNameTxt: String = &quot;&quot;
    @State var age: Int = 18
    
    var body: some View {
        VStack {
            Text(&quot;VIEW BUILDER EXAMPLE&quot;)
                .font(.title)
                .bold()
            
            Form {
                Section(&quot;BASIC&quot;) {
                    basicSection()
                }
                
                Section(&quot;USERS&quot;) {
                    usersSection()
                }
                
            }
        }
    }
    
    @ViewBuilder
    func basicSection() -&gt; some View {
        TextField(&quot;Full name&quot;, text: $fullNameTxt)
        
        Toggle(isOn: $isToggleOn, label: {
            Text(&quot;Dark mode&quot;)
        })
        
        Stepper(&quot;Age: \(age)&quot;, value: $age)
    }
    
    @ViewBuilder
    func usersSection() -&gt; some View {
        ForEach(listToShow, id: \.self) { item in
            Text(item)
        }
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ViewBuilderView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isToggleOn: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> listToShow: </span><span style="color: #ECEFF4">[</span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> [</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Kade Turner</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Ariella Briggs</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Kensley Carroll</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">]</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> fullNameTxt: </span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> age: </span><span style="color: #8FBCBB">Int</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">18</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">VIEW BUILDER EXAMPLE</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Form</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Section</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">BASIC</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">basicSection</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">Section</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">USERS</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">usersSection</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">ViewBuilder</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">basicSection</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">TextField</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Full name</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">text</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $fullNameTxt</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Toggle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">isOn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $isToggleOn, </span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Dark mode</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">})</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Stepper</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Age: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">age</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $age</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">ViewBuilder</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">func</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">usersSection</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">ForEach</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">listToShow, </span><span style="color: #88C0D0">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> \.</span><span style="color: #81A1C1">self</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> item </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">item</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h2 class="wp-block-heading">&nbsp;Is @ViewBuilder necessary?</h2>



<p>The short answer is no, but it’s a great idea to consider using them because it just makes your life a bit easier.</p>



<p>When you are using ViewBuilder you can easily split up your SwiftUI views inside the same class thereby making it easier for you to keep an overview.</p>



<h2 class="wp-block-heading">Wrap up SwiftUI ViewBuilder</h2>



<p>Using <code>ViewBuilder</code> when building a SwiftUI application is a great way for building efficient and clean user interfaces. It allows you to develop complex views effortlessly while maintaining readability and reducing boilerplate code. </p>



<p>Happy coding&nbsp;<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a href="https://softwareanders.com/swiftui-viewbuilder-organize-your-views/">SwiftUI ViewBuilder: Organize your views</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/swiftui-viewbuilder-organize-your-views/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI ClipShape: Shape Your Application</title>
		<link>https://softwareanders.com/swiftui-clipshape-shape-your-application/</link>
					<comments>https://softwareanders.com/swiftui-clipshape-shape-your-application/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Thu, 13 Jun 2024 17:43:30 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=653</guid>

					<description><![CDATA[<p>clipShape is a SwiftUI modifier that allows developers to define a shape that clips a view to shape it. This means that the view will be confined to the defined shape. You can create anything from circles to rectangles, it’s up to you. In this post you will learn about ClipShape in SwiftUI. You will [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/swiftui-clipshape-shape-your-application/">SwiftUI ClipShape: Shape Your Application</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>clipShape is a SwiftUI modifier that allows developers to define a shape that clips a view to shape it. This means that the view will be confined to the defined shape. You can create anything from circles to rectangles, it’s up to you.</p>



<p>In this post you will learn about ClipShape in SwiftUI. You will learn how to implement them and we will also cover the different shapes available. </p>



<h2 class="wp-block-heading">SwiftUI ClipShape example</h2>



<p>The best place to start any great tutorial is with a basic example, so let’s begin with a basic example of how to use the&nbsp;<code>.clipShape</code>&nbsp;modifier.</p>



<p>In the following example we will use&nbsp;<code>.clipShape</code>&nbsp;modifier to create a circle shape:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ClipShapeView: View {
    var body: some View {
        
        VStack {
            
        }
        .frame(width: 200, height: 200)
        .background(Color.red)
        .clipShape(.circle)
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ClipShapeView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">200</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">200</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">red</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">clipShape</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">circle</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="234" height="240" src="https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-05-07-at-20.48.34.png" alt="" class="wp-image-654"/></figure>



<p></p>



<h2 class="wp-block-heading">How to make rounded corners in&nbsp;SwiftUI?</h2>



<p>To create rounded corners in SwiftUI you should deffently use the&nbsp;<code>.clipShape</code>&nbsp;modifier. As you can see in the example above, you can use RoundedRectangle and set the cornerRadius. Use the cornerRadius to adjust how round the corners should be.</p>



<p>In the following example, we will create a VStack with rounded corners with the cornerRadius set to 40:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ClipShapeView: View {
    var body: some View {
        
        VStack {
            Text(&quot;Rounded corners&quot;)
                .bold()
                .font(.title)
        }
        .frame(width: 290, height: 150)
        .background(Color.red)
        .clipShape(RoundedRectangle(cornerRadius: 40))
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ClipShapeView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Rounded corners</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">290</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">150</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">red</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">clipShape</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedRectangle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">40</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="470" height="178" src="https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-05-07-at-20.54.47-1.png" alt="" class="wp-image-658" srcset="https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-05-07-at-20.54.47-1.png 470w, https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-05-07-at-20.54.47-1-300x114.png 300w" sizes="(max-width: 470px) 100vw, 470px" /></figure>



<p></p>



<h2 class="wp-block-heading">Different shapes to use in clipShape</h2>



<p>SwiftUI provides various different shapes we can choose from.</p>



<p>&nbsp;To be excat there are 6 different shapes in SwiftUI.</p>



<p>&nbsp;In the following section you will find the different shapes you can chose from.</p>



<h3 class="wp-block-heading">Rectangle</h3>



<p>In the following example we create a VStack with a red background and a rectangle shape:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ClipShapeView: View {
    var body: some View {
        
        VStack {
            Text(&quot;Rounded corners&quot;)
                .bold()
                .font(.title)
        }
        .frame(width: 290, height: 150)
        .background(Color.red)
        .clipShape(Rectangle())

    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ClipShapeView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Rounded corners</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">290</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">150</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">red</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">clipShape</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">Rectangle</span><span style="color: #ECEFF4">())</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="594" height="310" src="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.50.08.png" alt="" class="wp-image-673" srcset="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.50.08.png 594w, https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.50.08-300x157.png 300w" sizes="(max-width: 594px) 100vw, 594px" /></figure>



<h3 class="wp-block-heading">RoundedRectangle</h3>



<p>&nbsp;The rounded rectangle is property the most used shape and in this example we create a VStack with a red background and rounded conors:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ClipShapeView: View {
    var body: some View {
        
        VStack {
            Text(&quot;Rounded corners&quot;)
                .bold()
                .font(.title)
        }
        .frame(width: 290, height: 150)
        .background(Color.red)
        .clipShape(RoundedRectangle(cornerRadius: 20))
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ClipShapeView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Rounded corners</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">290</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">150</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">red</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">clipShape</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">RoundedRectangle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">20</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="636" height="340" src="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-11-at-15.57.15.png" alt="" class="wp-image-664" srcset="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-11-at-15.57.15.png 636w, https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-11-at-15.57.15-300x160.png 300w" sizes="(max-width: 636px) 100vw, 636px" /></figure>



<h3 class="wp-block-heading">Ellipse</h3>



<p>&nbsp;In this example we create a VStack with a red background and with a ellipse shape:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ClipShapeView: View {
    var body: some View {
        
        VStack {
            Text(&quot;Rounded corners&quot;)
                .bold()
                .font(.title)
        }
        .frame(width: 290, height: 150)
        .background(Color.red)
        .clipShape(Ellipse())
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ClipShapeView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Rounded corners</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">290</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">150</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">red</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">clipShape</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">Ellipse</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="594" height="310" src="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.51.33.png" alt="" class="wp-image-674" srcset="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.51.33.png 594w, https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.51.33-300x157.png 300w" sizes="(max-width: 594px) 100vw, 594px" /></figure>



<h3 class="wp-block-heading">Capsule</h3>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ClipShapeView: View {
    var body: some View {
        
        VStack {
            Text(&quot;Rounded corners&quot;)
                .bold()
                .font(.title)
        }
        .frame(width: 290, height: 150)
        .background(Color.red)
        .clipShape(Capsule())
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ClipShapeView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Rounded corners</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">290</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">150</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">red</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">clipShape</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">Capsule</span><span style="color: #ECEFF4">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p> In this example we create a VStack with a red background and with a circle shape:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="594" height="310" src="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.52.18.png" alt="" class="wp-image-676" srcset="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.52.18.png 594w, https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.52.18-300x157.png 300w" sizes="(max-width: 594px) 100vw, 594px" /></figure>



<h3 class="wp-block-heading">UnevenRoundedRectangle</h3>



<p>&nbsp;The uneven rounded rectangle is like the RoundedRectangle, but where only some of the conors is rounded.</p>



<p>&nbsp;In the following example we will create a VStack where the top right cornor is rounded and the bottom left cornor is rounded:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ClipShapeView: View {
    var body: some View {
        
        VStack {
            Text(&quot;Rounded corners&quot;)
                .bold()
                .font(.title)
        }
        .frame(width: 290, height: 150)
        .background(Color.red)
        .clipShape(UnevenRoundedRectangle(bottomLeadingRadius: 10, topTrailingRadius: 10))
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ClipShapeView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Rounded corners</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">bold</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">title</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">width</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">290</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">height</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">150</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">red</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">clipShape</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">UnevenRoundedRectangle</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">bottomLeadingRadius</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">10</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">topTrailingRadius</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">10</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="594" height="310" src="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.53.18.png" alt="" class="wp-image-678" srcset="https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.53.18.png 594w, https://softwareanders.com/wp-content/uploads/2024/06/Screenshot-2024-06-22-at-20.53.18-300x157.png 300w" sizes="(max-width: 594px) 100vw, 594px" /></figure>



<h2 class="wp-block-heading">Wrap ClipShape in&nbsp;SwiftUI</h2>



<p>The ClipShape is a powerful tool to create different shapes in your application. Apple has also made it easy to use and create various shapes.</p>



<p>In this post, we covered how to use the&nbsp;<code>.clipShape</code>&nbsp;modifier and as you should see — it is not difficult and quite straightforward to use.</p>



<p>I hope you can use this in your project — happy coding&nbsp;:-</p>
<p>The post <a href="https://softwareanders.com/swiftui-clipshape-shape-your-application/">SwiftUI ClipShape: Shape Your Application</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/swiftui-clipshape-shape-your-application/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI Long Press Gesture: Complete How-To Guide</title>
		<link>https://softwareanders.com/swiftui-long-press-how-to-guide/</link>
					<comments>https://softwareanders.com/swiftui-long-press-how-to-guide/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Tue, 07 May 2024 13:43:53 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=632</guid>

					<description><![CDATA[<p>The Long Press gesture in SwiftUI is a powerful interaction that triggers when a user presses and holds a view for a specific duration. This essential gesture recognizer is frequently implemented in modern iOS apps for context menus, drag and drop functionality, or revealing additional information to enhance user experience. SwiftUI makes implementing long press [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/swiftui-long-press-how-to-guide/">SwiftUI Long Press Gesture: Complete How-To Guide</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>The Long Press gesture in SwiftUI is a powerful interaction that triggers when a user presses and holds a view for a specific duration. This essential gesture recognizer is frequently implemented in modern iOS apps for context menus, drag and drop functionality, or revealing additional information to enhance user experience.</p>



<p>SwiftUI makes implementing long press interactions straightforward with the dedicated <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">.onLongPressGesture</mark></code> modifier. In this comprehensive tutorial, we&#8217;ll explore how to implement long press gestures in your SwiftUI applications, customize press duration, handle movement tolerance, and create responsive user interfaces.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<div class="ast-oembed-container " style="height: 100%;"><iframe title="SwiftUI Long Press - How to guide" width="1042" height="586" src="https://www.youtube.com/embed/wzkxVec3T10?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></div>
</div></figure>



<h2 class="wp-block-heading">How to add a Long Press Gesture in&nbsp;SwiftUI</h2>



<p>Let&#8217;s start with a basic example where we create an Image view displaying an SF Symbol. When you press and hold the image, the code will print &#8220;Long pressed&#8221; to the console:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct LongPressView: View {
    var body: some View {
        Image(systemName: &quot;button.horizontal.top.press.fill&quot;)
            .font(.system(size: 100))
            .onLongPressGesture() {
                print(&quot;Long pressed&quot;)
            }
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> LongPressView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">button.horizontal.top.press.fill</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">onLongPressGesture</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Long pressed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>That&#8217;s the fundamental implementation of the long press gesture in SwiftUI. The simplicity of this approach means you can easily attach it to any view in your application.</p>



<h2 class="wp-block-heading">Customize Long Press Gesture Duration</h2>



<p>While the default <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color"><code>.onLongPressGesture</code></mark> modifier works perfectly for many scenarios, you might need to adjust how long users must press before the action triggers. By default, SwiftUI sets this duration to 0.5 seconds.</p>



<p>In the following example, we&#8217;ll modify our previous implementation to require a longer 2-second press:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct LongPressView: View {
    var body: some View {
        Image(systemName: &quot;button.horizontal.top.press.fill&quot;)
            .font(.system(size: 100))
            .onLongPressGesture(minimumDuration: 2) {
                print(&quot;Long pressed&quot;)
            }
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> LongPressView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">button.horizontal.top.press.fill</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">onLongPressGesture</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">minimumDuration</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Long pressed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">minimumDuration</mark></code> parameter gives you precise control over the press timing, allowing you to create interactions that feel natural for different contexts in your app.</p>



<h2 class="wp-block-heading">Adjust Movement Tolerance with maximumDistance</h2>



<p>The <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">maximumDistance</mark></code> parameter of the <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">onLongPressGesture</mark></code> modifier defines how far users can move their finger during a long press without canceling the gesture. This tolerance setting is measured in points and defaults to 10 CGFloat points.</p>



<p>For applications where you want to be more forgiving of small movements during a press, you can increase this value as shown below:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct LongPressView: View {
    var body: some View {
        Image(systemName: &quot;button.horizontal.top.press.fill&quot;)
            .font(.system(size: 100))
            .onLongPressGesture(maximumDistance: 100) {
                print(&quot;Long pressed&quot;)
            }
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> LongPressView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">button.horizontal.top.press.fill</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">onLongPressGesture</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">maximumDistance</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Long pressed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>This larger 100-point tolerance creates a more forgiving long press experience, particularly useful for users who might have difficulty keeping their finger perfectly still.</p>



<h2 class="wp-block-heading">Troubleshooting Common Issues</h2>



<p>When implementing long press gestures in SwiftUI, you might encounter some challenges. Here are solutions to the most common problems developers face:</p>



<h3 class="wp-block-heading">Gesture Not Triggering Consistently</h3>



<p>If your long press gesture isn&#8217;t triggering reliably, check for these common issues:</p>



<ul class="wp-block-list">
<li><strong>Overlapping Views</strong>: Make sure you don&#8217;t have transparent or overlapping views that might be intercepting the gesture.</li>



<li><strong>Gesture Priorities</strong>: When using multiple gestures, set proper priorities using <code>.highPriorityGesture()</code> or <code>.simultaneousGesture()</code> modifiers.</li>



<li><strong>View Size</strong>: Ensure your view has sufficient size to capture touches. Small tap targets can cause inconsistent gesture recognition.</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="// Example of fixing gesture conflicts with priorities
Image(systemName: &quot;button.horizontal.top.press.fill&quot;)
    .font(.system(size: 100))
    // The tap gesture won't interfere with the long press
    .onTapGesture {
        print(&quot;Tapped&quot;)
    }
    .highPriorityGesture(
        LongPressGesture(minimumDuration: 0.5)
            .onEnded { _ in
                print(&quot;Long pressed with priority&quot;)
            }
    )" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// Example of fixing gesture conflicts with priorities</span></span>
<span class="line"><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">button.horizontal.top.press.fill</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// The tap gesture won&#39;t interfere with the long press</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">onTapGesture</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Tapped</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">highPriorityGesture</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">LongPressGesture</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">minimumDuration</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0.5</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">onEnded</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> _ </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Long pressed with priority</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">)</span></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">Handling Gesture State Updates</h3>



<p>For more responsive interfaces, you might want to track the state of a long press:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct LongPressStateView: View {
    @State private var isPressed = false
    
    var body: some View {
        Image(systemName: &quot;button.horizontal.top.press.fill&quot;)
            .font(.system(size: 100))
            .scaleEffect(isPressed ? 1.5 : 1.0)
            .animation(.spring(), value: isPressed)
            .onLongPressGesture(minimumDuration: 1.0, pressing: { pressing in
                isPressed = pressing
            }) {
                print(&quot;Long press completed&quot;)
            }
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> LongPressStateView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isPressed </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">button.horizontal.top.press.fill</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">100</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">scaleEffect</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">isPressed </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1.5</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1.0</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">animation</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">spring</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> isPressed</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">onLongPressGesture</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">minimumDuration</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1.0</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">pressing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> pressing </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                isPressed </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> pressing</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Long press completed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">Gesture Not Working Inside ScrollView</h3>



<p>Long press gestures inside ScrollViews can be problematic because the ScrollView might capture the touches. Use <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">.simultaneousGesture()</mark></code> to allow both to work:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="ScrollView {
    ForEach(items, id: \.self) { item in
        Text(item)
            .frame(maxWidth: .infinity)
            .padding()
            .background(Color.gray.opacity(0.2))
            .simultaneousGesture(
                LongPressGesture()
                    .onEnded { _ in
                        print(&quot;Long pressed item: \(item)&quot;)
                    }
            )
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">ScrollView</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ForEach</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">items, </span><span style="color: #88C0D0">id</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> \.</span><span style="color: #81A1C1">self</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> item </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">item</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">frame</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">maxWidth</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .infinity</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">gray</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">opacity</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">0.2</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">simultaneousGesture</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #88C0D0">LongPressGesture</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    .</span><span style="color: #88C0D0">onEnded</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> _ </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">                        </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Long pressed item: </span><span style="color: #81A1C1">\(</span><span style="color: #A3BE8C">item</span><span style="color: #81A1C1">)</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h2 class="wp-block-heading">iOS Version Compatibility</h2>



<p>SwiftUI&#8217;s gesture handling has evolved across iOS versions, with important differences to consider when implementing long press functionality:</p>



<h3 class="wp-block-heading">iOS 13 (Initial SwiftUI Release)</h3>



<ul class="wp-block-list">
<li>Basic long press gesture support with <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">onLongPressGesture()</mark></code></li>



<li>Limited control over gesture state during the press</li>



<li>Simpler gesture system with fewer customization options</li>
</ul>



<h3 class="wp-block-heading">iOS 14 Improvements</h3>



<ul class="wp-block-list">
<li>Added the ability to track press state with the <code>pressing</code> parameter</li>



<li>Better integration with other gestures</li>



<li>Improved gesture performance and reliability</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="// iOS 14+ approach with pressing state parameter
.onLongPressGesture(minimumDuration: 1.0, pressing: { isPressing in
    // This closure is called when the press state changes
    withAnimation {
        self.isPressed = isPressing
    }
}) {
    // This is called when the long press completes
    self.handleLongPress()
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// iOS 14+ approach with pressing state parameter</span></span>
<span class="line"><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">onLongPressGesture</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">minimumDuration</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1.0</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">pressing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> isPressing </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// This closure is called when the press state changes</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">withAnimation</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">isPressed</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> isPressing</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// This is called when the long press completes</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">self</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">handleLongPress</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h3 class="wp-block-heading">iOS 15 and 16 Enhancements</h3>



<ul class="wp-block-list">
<li>Better gesture sequencing capabilities</li>



<li>Improved performance with complex gesture chains</li>



<li>Enhanced support for mixed gesture types</li>
</ul>



<h3 class="wp-block-heading">iOS 17 Latest Features</h3>



<ul class="wp-block-list">
<li>Better accessibility integration with gestures</li>



<li>Improved haptic feedback controls</li>



<li>More reliable gesture state management</li>
</ul>



<p>For maximum compatibility across iOS versions, consider implementing fallbacks for older versions:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct CompatibleLongPressView: View {
    @State private var isPressed = false
    @Environment(\.horizontalSizeClass) var sizeClass
    
    var body: some View {
        if #available(iOS 14, *) {
            // Use enhanced iOS 14+ approach
            buttonWithModernLongPress
        } else {
            // Fallback for iOS 13
            buttonWithBasicLongPress
        }
    }
    
    @ViewBuilder
    var buttonWithModernLongPress: some View {
        Button(&quot;Press and Hold Me&quot;) {
            // Button action
        }
        .padding()
        .background(isPressed ? Color.blue : Color.gray)
        .foregroundColor(.white)
        .cornerRadius(8)
        .onLongPressGesture(minimumDuration: 1.0, pressing: { pressing in
            isPressed = pressing
        }) {
            print(&quot;Long press completed&quot;)
        }
    }
    
    var buttonWithBasicLongPress: some View {
        Button(&quot;Press and Hold Me&quot;) {
            // Button action
        }
        .padding()
        .background(Color.gray)
        .foregroundColor(.white)
        .cornerRadius(8)
        .onLongPressGesture {
            isPressed = true
            print(&quot;Long press completed&quot;)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                isPressed = false
            }
        }
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> CompatibleLongPressView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">private</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isPressed </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">Environment</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">\.</span><span style="color: #D8DEE9">horizontalSizeClass</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> sizeClass</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">#available</span><span style="color: #ECEFF4">(</span><span style="color: #81A1C1">iOS</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">14</span><span style="color: #D8DEE9FF">, </span><span style="color: #81A1C1">*</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Use enhanced iOS 14+ approach</span></span>
<span class="line"><span style="color: #D8DEE9FF">            buttonWithModernLongPress</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Fallback for iOS 13</span></span>
<span class="line"><span style="color: #D8DEE9FF">            buttonWithBasicLongPress</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">ViewBuilder</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> buttonWithModernLongPress: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Press and Hold Me</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Button action</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">isPressed </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> Color.</span><span style="color: #D8DEE9">blue</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Color.</span><span style="color: #D8DEE9">gray</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">onLongPressGesture</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">minimumDuration</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1.0</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">pressing</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> pressing </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">            isPressed </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> pressing</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">})</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Long press completed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> buttonWithBasicLongPress: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Button</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Press and Hold Me</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">            </span><span style="color: #616E88">// Button action</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">padding</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">gray</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">foregroundColor</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">white</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">cornerRadius</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">8</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">onLongPressGesture</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            isPressed </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">print</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Long press completed</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            DispatchQueue.</span><span style="color: #D8DEE9">main</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">asyncAfter</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">deadline</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #88C0D0">now</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0.1</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">                isPressed </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h2 class="wp-block-heading">Advanced Long Press Implementation Tips</h2>



<p>For more sophisticated interactions, consider combining long press gestures with:</p>



<ul class="wp-block-list">
<li>State changes to provide visual feedback during the press</li>



<li>Haptic feedback to enhance the tactile experience</li>



<li>Animation transitions when revealing context menus</li>



<li>Combining with drag gestures for drag-and-drop functionality</li>
</ul>



<h2 class="wp-block-heading">Wrap Up: Long Press in SwiftUI</h2>



<p>The long press gesture is a versatile interaction mechanism that can enhance your SwiftUI applications in numerous ways. As we&#8217;ve demonstrated, implementing the <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-ast-global-color-5-color">.onLongPressGesture</mark></code> modifier is straightforward, and the customization options allow you to fine-tune the experience to your specific requirements.</p>



<p>Whether you&#8217;re building contextual menus, implementing drag and drop, or creating innovative new interaction paradigms, mastering the long press gesture will help you create more intuitive and responsive iOS applications. </p>



<p>Happy coding! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a href="https://softwareanders.com/swiftui-long-press-how-to-guide/">SwiftUI Long Press Gesture: Complete How-To Guide</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/swiftui-long-press-how-to-guide/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI Toggle: Turn On And Off</title>
		<link>https://softwareanders.com/swiftui-toggle-turn-on-and-off/</link>
					<comments>https://softwareanders.com/swiftui-toggle-turn-on-and-off/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Thu, 02 May 2024 06:25:08 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=626</guid>

					<description><![CDATA[<p>SwiftUI Toggle is a UI control that enables users to switch between two states, typically represented as on and off. If you are building a view where you can enable a feature, then the Toggle view is the way to go. With very little code you can implement a simple and effective way of getting [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/swiftui-toggle-turn-on-and-off/">SwiftUI Toggle: Turn On And Off</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>SwiftUI Toggle is a UI control that enables users to switch between two states, typically represented as on and off. If you are building a view where you can enable a feature, then the Toggle view is the way to go.</p>



<p>With very little code you can implement a simple and effective way of getting user input. In this blog post we will cover how to implement a Toggle switch, change the color, hide the label and execute a function when turned on.</p>



<h2 class="wp-block-heading">SwiftUI Toggle&nbsp;example</h2>



<p>Let’s start with the most basic example of a Toggle in SwiftUI. Using Toggle is straightforward first define a state variable that holds the state of the toggle and then create a toggle view and bind the variable.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ToggleView: View {
    @State var isToggleOn: Bool = false
    
    var body: some View {
        Toggle(&quot;Basic toggle example&quot;, isOn: $isToggleOn)
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ToggleView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isToggleOn: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Toggle</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Basic toggle example</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isOn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $isToggleOn</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="520" height="56" src="https://softwareanders.com/wp-content/uploads/2024/05/ScreenRecording2024-04-24at15.00.31-ezgif.com-optimize.gif" alt="" class="wp-image-627"/></figure>



<p></p>



<h2 class="wp-block-heading">SwiftUI Toggle without&nbsp;label</h2>



<p>Sometimes you just want a toggle without a label. When you want to create a Toggle there are two ways you can go:</p>



<ol class="wp-block-list">
<li>Create a Toggle with an empty string.</li>



<li>Create a Toggle and use the .labelsHidden() modifier.</li>
</ol>



<p>The only difference is if you choose option 1, the toggle view will use the full width, and if you use option it won’t.&nbsp;</p>



<p>In the following example, you can see the two different options:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ToggleView: View {
    @State var isToggleOn: Bool = false
    
    var body: some View {
        Text(&quot;Empty string&quot;)
        Toggle(&quot;&quot;, isOn: $isToggleOn)
            .background(Color.blue)
        
        VStack {
            Text(&quot;labelsHidden modifier&quot;)
            Toggle(&quot;&quot;, isOn: $isToggleOn)
                .labelsHidden()
                .background(Color.blue)
        }
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ToggleView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isToggleOn: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Empty string</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Toggle</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isOn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $isToggleOn</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">blue</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">VStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">labelsHidden modifier</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Toggle</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isOn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $isToggleOn</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">labelsHidden</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">background</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">Color.</span><span style="color: #D8DEE9">blue</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="520" height="262" src="https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-04-24-at-15.12.13.png" alt="" class="wp-image-628" srcset="https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-04-24-at-15.12.13.png 520w, https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-04-24-at-15.12.13-300x151.png 300w" sizes="(max-width: 520px) 100vw, 520px" /></figure>



<p></p>



<h2 class="wp-block-heading">SwiftUI toggle&nbsp;color</h2>



<p>You can easily make your application unique by just changing the color of the Toggle. To change the color of the toggle button, when it’s enabled, all you need to do is to add the&nbsp;.tint modifier.</p>



<p>In the following example, we will change the active color of the toggle to red:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct ToggleView: View {
    @State var isToggleOn: Bool = false
    
    var body: some View {
        Toggle(&quot;Color tint&quot;, isOn: $isToggleOn)
            .tint(.red)
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> ToggleView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isToggleOn: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Toggle</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Color tint</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isOn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $isToggleOn</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">tint</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">red</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="520" height="62" src="https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-04-28-at-23.19.01.png" alt="" class="wp-image-629" srcset="https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-04-28-at-23.19.01.png 520w, https://softwareanders.com/wp-content/uploads/2024/05/Screenshot-2024-04-28-at-23.19.01-300x36.png 300w" sizes="(max-width: 520px) 100vw, 520px" /></figure>



<p></p>



<h2 class="wp-block-heading">SwiftUI toggle&nbsp;callback</h2>



<p>When working with the toggle, you can either set a boolean value by toggling or call a function when the toggle is set.</p>



<p>You can use no action right off the bat, but you can use the&nbsp;<code>.onChange</code>&nbsp;modifier.</p>



<p>In the following example, we will call a function called&nbsp;<code>toggleAction()</code>&nbsp;when the toggle is set to true — the two examples provide the same result, but one is for iOS17 and the other is for iOS before 17:</p>



<p>iOS17+:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="Toggle(&quot;onChange&quot;, isOn: $isToggleOn)
    .onChange(of: isToggleOn) { oldValue, newValue in
        if newValue {
            toggleAction()
        }
    }" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">Toggle</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">onChange</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isOn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $isToggleOn</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">onChange</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">of</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> isToggleOn</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> oldValue, newValue </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> newValue </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">toggleAction</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>Before iOS17:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="Toggle(&quot;onChange&quot;, isOn: $isToggleOn)
    .onChange(of: isToggleOn) { result in
        if result {
            toggleAction()
        }
    }" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">Toggle</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">onChange</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">isOn</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> $isToggleOn</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">onChange</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">of</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> isToggleOn</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> result </span><span style="color: #81A1C1">in</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> result </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">toggleAction</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p></p>



<h2 class="wp-block-heading">Wrap up SwiftUI&nbsp;Toggle</h2>



<p>The toggle view is a great tool and perfect if you want your users to turn something on or off. Like changing from dark to light mode in your application.</p>



<p>I hope you in this guide learned how easy it is to implement a toggle in SwiftUI. We also covered how to create a toggle without a label, change the color, and call a function when the toggle is activated.</p>



<p>Thank you for reading this post about toggle and happy coding&nbsp;<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a href="https://softwareanders.com/swiftui-toggle-turn-on-and-off/">SwiftUI Toggle: Turn On And Off</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/swiftui-toggle-turn-on-and-off/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SwiftUI Animate SF Symbols</title>
		<link>https://softwareanders.com/swiftui-animate-sf-symbols/</link>
					<comments>https://softwareanders.com/swiftui-animate-sf-symbols/#respond</comments>
		
		<dc:creator><![CDATA[Brahe]]></dc:creator>
		<pubDate>Wed, 24 Apr 2024 11:51:57 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://softwareanders.com/?p=620</guid>

					<description><![CDATA[<p>SF Symbols is a great solution for making visual tasks with developing an iOS application. Not only are they great, but there are so many different ones to choose from. If you want to make your SF Symbols stand out a bit more or create some kind of visual effect, then animating your SF Symbols [&#8230;]</p>
<p>The post <a href="https://softwareanders.com/swiftui-animate-sf-symbols/">SwiftUI Animate SF Symbols</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p>SF Symbols is a great solution for making visual tasks with developing an iOS application. Not only are they great, but there are so many different ones to choose from. If you want to make your SF Symbols stand out a bit more or create some kind of visual effect, then animating your SF Symbols is the way to go.</p>



<p>In the following examples, we will discover different variations of animations, so you are ready to animate your SF Symbols.</p>



<h2 class="wp-block-heading">How to animate SF Symbols in&nbsp;SwiftUI</h2>



<p>As of iOS17 Apple has made it easy for us to apply animations to the SF Symbols. We can utilize the symbolEffect modifier and then apply many different animations.</p>



<h3 class="wp-block-heading">Pulse animation&nbsp;</h3>



<p>Pulse animations provide instant visual feedback to users, indicating that an action has been completed or something is happening in the background. This pulse animation enhances the user experience by reducing uncertainty and providing a sense of responsiveness.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="Image(systemName: &quot;trash&quot;)
    .font(.system(size: 50, weight: .bold))
    .symbolEffect(.pulse, value: 1)" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">trash</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">50</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">bold</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">symbolEffect</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">pulse</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #ECEFF4">)</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="600" height="124" src="https://softwareanders.com/wp-content/uploads/2024/04/ScreenRecording2024-04-22at08.22.01-ezgif.com-optimize.gif" alt="" class="wp-image-621"/></figure>



<p></p>



<h3 class="wp-block-heading">Bounce animation</h3>



<p>Like pulse animations, bounce animations provide visual feedback to users, indicating that an action has been acknowledged or completed. This feedback gives the users a feeling that their interactions with the app are being registered and enhancing the overall user experience.</p>



<p>The bounce animation is often used when an item has been deleted or added.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="Image(systemName: &quot;trash&quot;)
    .font(.system(size: 50, weight: .bold))
    .symbolEffect(.bounce, value: 1)" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">trash</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">50</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">bold</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    .</span><span style="color: #88C0D0">symbolEffect</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">bounce</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span><span style="color: #ECEFF4">)</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="600" height="124" src="https://softwareanders.com/wp-content/uploads/2024/04/ScreenRecording2024-04-22at08.23.57-ezgif.com-video-to-gif-converter.gif" alt="" class="wp-image-622"/></figure>



<p></p>



<h3 class="wp-block-heading">variableColor animation</h3>



<p>Variable color animation type is cool because it changes the opacity of the variable layers in the SF Symbol making the symbol stand out and making your application come to life.</p>



<p>There are many different ways you can use this type, in the following example we will create a wifi symbol and create 4 different animations using the variableColor effect:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct SFSymbolsView: View {
    @State var valueToUse: Int = 0
    var body: some View {
        HStack {
            Image(systemName: &quot;wifi&quot;)
                .font(.system(size: 50, weight: .bold))
                .symbolEffect(.variableColor, value: valueToUse)
            
            Image(systemName: &quot;wifi&quot;)
                .font(.system(size: 50, weight: .bold))
                .symbolEffect(.variableColor.iterative, value: valueToUse)
            
            Image(systemName: &quot;wifi&quot;)
                .font(.system(size: 50, weight: .bold))
                .symbolEffect(.variableColor.hideInactiveLayers, value: valueToUse)
    
        }
                
        Button {
            valueToUse+=1
        }label: {
            Text(&quot;Make animation&quot;)
        }
        .buttonStyle(.borderedProminent)
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> SFSymbolsView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> valueToUse: </span><span style="color: #8FBCBB">Int</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">HStack</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">wifi</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">50</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">bold</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">symbolEffect</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">variableColor</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> valueToUse</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">wifi</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">50</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">bold</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">symbolEffect</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">variableColor</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">iterative</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> valueToUse</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">wifi</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">50</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">bold</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">                .</span><span style="color: #88C0D0">symbolEffect</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">variableColor</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">hideInactiveLayers</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">value</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> valueToUse</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Button</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            valueToUse</span><span style="color: #81A1C1">+=</span><span style="color: #B48EAD">1</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Make animation</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">buttonStyle</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">borderedProminent</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="600" height="154" src="https://softwareanders.com/wp-content/uploads/2024/04/ScreenRecording2024-04-22at08.39.30-ezgif.com-optimize.gif" alt="" class="wp-image-623"/></figure>



<p></p>



<h3 class="wp-block-heading">Replace animation</h3>



<p>Replace animations can make transitions between two SF Symbols smoother and more visually appealing. Instead of SF Symbols abruptly appearing or disappearing, a replace animation can create a seamless flow from one SF Symbol to another, enhancing the overall user experience.</p>



<p>In the following example we will have a trash can and replace it with a trash can with a slash:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import SwiftUI

struct SFSymbolsView: View {
    @State var isDeleted: Bool = false
    var body: some View {
        Image(systemName: isDeleted ? &quot;trash.slash&quot; : &quot;trash&quot;)
            .font(.system(size: 50, weight: .bold))
            .contentTransition(.symbolEffect(.replace))
        
        
        Button {
            isDeleted.toggle()
        }label: {
            Text(&quot;Delete/Undelete&quot;)
        }
        .buttonStyle(.bordered)
    }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> SwiftUI</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">struct</span><span style="color: #D8DEE9FF"> SFSymbolsView</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB; font-weight: bold">View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">@</span><span style="color: #81A1C1">State</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> isDeleted: </span><span style="color: #8FBCBB">Bool</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">var</span><span style="color: #D8DEE9FF"> body: </span><span style="color: #81A1C1">some</span><span style="color: #D8DEE9FF"> View </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Image</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">systemName</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> isDeleted </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">trash.slash</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">trash</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">font</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">system</span><span style="color: #ECEFF4">(</span><span style="color: #88C0D0">size</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">50</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">weight</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> .</span><span style="color: #D8DEE9">bold</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">            .</span><span style="color: #88C0D0">contentTransition</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #88C0D0">symbolEffect</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">replace</span><span style="color: #ECEFF4">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">Button</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            isDeleted.</span><span style="color: #88C0D0">toggle</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span><span style="color: #88C0D0">label</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #88C0D0">Text</span><span style="color: #ECEFF4">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Delete/Undelete</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        .</span><span style="color: #88C0D0">buttonStyle</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9FF">.</span><span style="color: #D8DEE9">bordered</span><span style="color: #ECEFF4">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>The result:</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="336" height="248" src="https://softwareanders.com/wp-content/uploads/2024/04/ScreenRecording2024-04-23at21.38.01-ezgif.com-optimize.gif" alt="" class="wp-image-624"/></figure>



<p></p>



<h2 class="wp-block-heading">Wrap up SwiftUI SF Animation</h2>



<p>Applying animation to your SF Symbols makes your application come to life and gives the user a visual indication that something is happening. In this blog post, we covered the most common animations you can apply to the SF Symbols.</p>



<p>I hope you can use this in your application — happy coding&nbsp;<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a href="https://softwareanders.com/swiftui-animate-sf-symbols/">SwiftUI Animate SF Symbols</a> appeared first on <a href="https://softwareanders.com">softwareanders.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://softwareanders.com/swiftui-animate-sf-symbols/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/


Served from: softwareanders.com @ 2026-05-20 04:31:45 by W3 Total Cache
-->