<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Scott Smith Dev]]></title><description><![CDATA[Articles, podcast episodes, and videos about iOS development. Swift, SwiftUI, everything Apple platform development.]]></description><link>https://scottsmithdev.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1706074167186/5UujeRTnG.png</url><title>Scott Smith Dev</title><link>https://scottsmithdev.com</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Jul 2026 09:42:31 GMT</lastBuildDate><atom:link href="https://scottsmithdev.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Shrink Images for Accessibility in SwiftUI]]></title><description><![CDATA[Have you ever built a screen layout where there's a big Image above a couple of Text views? There's a good chance that you have because this layout pattern is commonly used in places like app intro screens and onboarding screens. Something kind of li...]]></description><link>https://scottsmithdev.com/shrink-images-for-accessibility-in-swiftui</link><guid isPermaLink="true">https://scottsmithdev.com/shrink-images-for-accessibility-in-swiftui</guid><category><![CDATA[SwiftUI]]></category><dc:creator><![CDATA[Scott Smith]]></dc:creator><pubDate>Tue, 20 Feb 2024 21:25:58 GMT</pubDate><content:encoded><![CDATA[<p>Have you ever built a screen layout where there's a big <code>Image</code> above a couple of <code>Text</code> views? There's a good chance that you have because this layout pattern is commonly used in places like app intro screens and onboarding screens. Something kind of like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708458260975/cd861164-742a-4527-a5de-ce0ec93b17f7.png" alt class="image--center mx-auto" /></p>
<p>There are often more elements on the screen than what my screenshot shows, but I want to keep this demo simple. 😊</p>
<p>The most recent time I was building screens like that, I was testing how the layout responded to Dynamic Type (i.e. bigger/smaller device font size setting). And it was in that exact moment when I realized that the most important part of my layout was the <strong><em>copy</em></strong> being shown in the <code>Text</code> views and <strong><em>not</em></strong> the <code>Image</code>. Because the images－in my specific situation－did not provide any informational value to the user. And I really wanted the user to be able to focus on reading the <em>text</em> without having the <em>image</em> get in the way when using larger Dynamic Type sizes. With this realization, my brain immediately thought, "OK, what I need to do is leverage Dynamic Type to <strong><em>shrink</em></strong> the height of the <strong>image</strong> as the Dynamic Type size <strong><em>grows</em></strong>." I had actually never thought about doing this before, and I also don't think it's a documented approach in the <a target="_blank" href="https://developer.apple.com/design/human-interface-guidelines">HIG</a> or anything, but it seemed to make sense in <em>my</em> situation at the time and I already knew the exact tools to use in SwiftUI to make it a reality. So I decided to go ahead with it. And that's the approach I wanted to explore in today's article!</p>
<p>In order to accomplish this, we only need to use 3 things:</p>
<p>1️⃣. The awesome <code>Environment</code> variable called <code>dynamicTypeSize</code> to access the user's current font size;</p>
<pre><code class="lang-swift">@<span class="hljs-type">Environment</span>(\.dynamicTypeSize) <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> currentDynamicTypeSize
</code></pre>
<p>(I love how easily we can access Dynamic Type information! 😃)</p>
<p>2️⃣. A <code>@State</code> property to hold a calculated image height－based on the <code>currentDynamicTypeSize</code>;</p>
<pre><code class="lang-swift">@<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> calculatedImageHeight: <span class="hljs-type">Double</span> = .zero <span class="hljs-comment">// arbitrary initial value</span>
</code></pre>
<p>3️⃣. Somewhere to <code>switch</code> on the <code>currentDynamicTypeSize</code>, like the <code>.onChange</code> modifier or the <code>.onAppear</code> modifier (I chose <code>.onChange</code>, but you do you), where we can calculate and set the <code>calculatedImageHeight</code>.</p>
<pre><code class="lang-swift">.onChange(of: currentDynamicTypeSize, initial: <span class="hljs-literal">true</span>, { <span class="hljs-number">_</span>, newValue <span class="hljs-keyword">in</span>
    <span class="hljs-keyword">switch</span> newValue {
        ...
        <span class="hljs-comment">// case .small, .medium, .large － etc.</span>
        <span class="hljs-comment">// Calculate and set the image height here</span>
    }
})
</code></pre>
<p>And with those 3 key things, all we have left to do is calculate a <em>height</em> based on the different Dynamic Type sizes and then use it on the <code>Image</code>! To do that, we can let Xcode auto-fill in all the <code>case</code>s in the <code>switch</code> statement so we can see all the possible Dynamic Type sizes, and then do a quick little multiplication arithmetic to come up with a value for <code>calculatedImageHeight</code>.</p>
<p>The end goal is to have the <code>calculatedImageHeight</code> be a certain <em>percentage</em> of a <em>predetermined</em> base image height. Let's choose <code>268</code> for this demo. (e.g. if we want the <em>calculated</em> height to be 75% of 268, that's <code>0.75 * 268</code>) So, we can choose the Dynamic Type sizes that fit our particular situation, decide on a "scale factor" percentage for each case, then multiply <code>baseImageHeight</code> by the <code>scaleFactor</code>—and that's our <code>calculatedImageHeight</code>! Here's an example of how that could look:</p>
<pre><code class="lang-swift">.onChange(of: currentDynamicTypeSize, initial: <span class="hljs-literal">true</span>, { <span class="hljs-number">_</span>, newValue <span class="hljs-keyword">in</span>
    <span class="hljs-keyword">let</span> scaleFactor: <span class="hljs-type">Double</span>

    <span class="hljs-keyword">switch</span> newValue {
    <span class="hljs-keyword">case</span> .xSmall, .small, .medium, .large:
        scaleFactor = <span class="hljs-number">1.0</span>
    <span class="hljs-keyword">case</span> .xLarge:
        scaleFactor = <span class="hljs-number">0.9</span>
    <span class="hljs-keyword">case</span> .xxLarge:
        scaleFactor = <span class="hljs-number">0.75</span>
    <span class="hljs-keyword">case</span> .xxxLarge:
        scaleFactor = <span class="hljs-number">0.55</span>
    <span class="hljs-keyword">default</span>:
        scaleFactor = <span class="hljs-number">0.3</span>
    }

    <span class="hljs-keyword">let</span> baseImageHeight = <span class="hljs-number">268.0</span>
    calculatedImageHeight = baseImageHeight * scaleFactor
})
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">This is <strong><em>not </em></strong>an exhaustive list of every Dynamic Type size. These are sizes and values used for this demo. And, yep, you could totally put that <code>baseImageHeight</code> into a constant up near the <code>@State</code> variable, if you wanted to. Some people might like having it near the calculation, while others might like it up top near the related <code>calculatedImageHeight</code> property. I'm not picking a side here. This is nice for the demo, so let's not worry about it, k?</div>
</div>

<p>Notice, as the Dynamic Type size gets <strong><em>bigger</em></strong>, we use <strong><em>smaller</em></strong> percentages: 90%, down to 75%, down to 55%, and then to 30%. For this demo, I decided to cap the scaling off after <code>.xxxLarge</code> by putting 30% into the <code>default</code> case. So in the end, any Dynamic Type size <strong><em>smaller</em></strong> than <code>.xLarge</code> uses 100% of the base image height; anything <strong><em>bigger</em></strong> than <code>.xxxLarge</code> uses 30% of the base image height; and a few other percentages for the cases in between.</p>
<p>Now we're ready to apply <code>calculatedImageHeight</code> to the <code>Image</code> and see what happens!</p>
<pre><code class="lang-swift"><span class="hljs-type">Image</span>(.balloons)
    .resizable()
    .scaledToFit()
    .frame(maxHeight: calculatedImageHeight) <span class="hljs-comment">// 🎉 ✅</span>
</code></pre>
<p>And hey, it works! Here's the result as a GIF:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708461349796/d4567e3c-ebec-4faf-ad30-aef78d603422.gif" alt class="image--center mx-auto" /></p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Play around with the <code>scaleFactor</code> percentages to see what feels best to you. These example percentages are for demo purposes.</div>
</div>

<p>I understand that there's a chance another approach exists to achieve this exact same thing. 😄 I do think, though, that this approach is a fun way to leverage Dynamic Type to achieve a specific－and hopefully <em>improved</em>－user experience.</p>
<p>That's all! See ya later, rollerblader!</p>
<p>Come say "Hi!" on <a target="_blank" href="https://twitter.com/ScottSmithDev">Twitter</a> or <a target="_blank" href="https://mastodon.social/@ScottSmithDev">Mastodon</a>! Or don't. 😄</p>
]]></content:encoded></item><item><title><![CDATA[Encapsulate and Generalize in Swift]]></title><description><![CDATA[Whenever I use a 3rd-party dependency in my app, I try my best to import that dependency only one single time in the entire project. I also never use the actual dependency name in my variable names, class names, or any other names. In other words, I ...]]></description><link>https://scottsmithdev.com/encapsulate-and-generalize-in-swift</link><guid isPermaLink="true">https://scottsmithdev.com/encapsulate-and-generalize-in-swift</guid><category><![CDATA[Swift]]></category><category><![CDATA[SwiftUI]]></category><dc:creator><![CDATA[Scott Smith]]></dc:creator><pubDate>Tue, 13 Feb 2024 08:00:45 GMT</pubDate><content:encoded><![CDATA[<p>Whenever I use a 3rd-party dependency in my app, I try my best to <code>import</code> that dependency only <strong>one single time</strong> in the <em>entire</em> project. I also never use the actual dependency name in my variable names, class names, or any other names. In other words, I <strong>encapsulate</strong> and <strong>generalize</strong>.</p>
<p>Come along if you'd like to see what I mean. 😊</p>
<h3 id="heading-lets-start-with-this-works-and-then-well-improve-it">Let's start with "this works" (and then we'll improve it)</h3>
<p>(To be clear, the next few code snippets are not the final result. They'll be improved down below.) Here's an example "service" class whose job is to use APIs from AcmeSupport to implement customer support features directly in your app ("AcmeSupport" is a fake 3rd-party dependency name I thought of because I have Roadrunner cartoons on my mind).</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> AcmeSupport

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AcmeSupportService</span> </span>{
    ...
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchAcmeTickets</span><span class="hljs-params">()</span></span> -&gt; async [<span class="hljs-type">AcmeTicket</span>] {
        <span class="hljs-comment">// Call into the AcmeSupport API here</span>
        ...
    }
}
</code></pre>
<p>As you can see, our method calls into Acme's API and returns an array of AcmeSupport's own type called <code>AcmeTicket</code>. It looks like we could use that type nicely, so we'll go ahead and use this service class in some <em>other</em> part of the app, like this:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> AcmeSupport

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataLogicPlace</span> </span>{
    ...
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> acmeService = <span class="hljs-type">AcmeSupportService</span>()
    ...
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loadAcmeTickets</span><span class="hljs-params">()</span></span> async -&gt; [<span class="hljs-type">AcmeTicket</span>] {
        <span class="hljs-keyword">let</span> acmeTickets = await acmeService.fetchAcmeTickets()
        <span class="hljs-comment">// Here you can minpulate the `AcmeTicket` objects </span>
        <span class="hljs-comment">// from the `acmeTickets` array</span>
        ...
    }
    ...
}
</code></pre>
<p>It's possible you may want to do <em>other</em> things with the <code>AcmeTicket</code> type from the <code>acmeTickets</code> array result that <em>could</em> require you to <code>import AcmeSupport</code>. So the <code>DataLogicPlace</code> class imports it. And since we want to display these tickets in our UI, we can even use these <code>AcmeTicket</code> types right inside our <code>View</code>:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI
<span class="hljs-keyword">import</span> AcmeSupport

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">AcmeSupportScreen</span>: <span class="hljs-title">View</span> </span>{
    ...
    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        ...
        <span class="hljs-type">ForEach</span>(acmeTickets) { acmeTicket <span class="hljs-keyword">in</span>
            acmeTicketRowView(forAcmeTicket: acmeTicket)
        }
        ...
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">acmeTicketRowView</span><span class="hljs-params">(forAcmeTicket: AcmeTicket)</span></span> -&gt; some <span class="hljs-type">View</span> {
        ...
    }
}
</code></pre>
<p>And for this <code>View</code> (i.e. "<a target="_blank" href="https://scottsmithdev.com/screen-vs-view-in-swiftui">Screen</a>" 😉), we have to <code>import AcmeSupport</code> so we can access its <code>AcmeTicket</code> type to use as the argument type in our <code>acmeTicketRowView</code> method.</p>
<p>All of this code may already look great to you. And that's totally OK. This works. As planned though, we'll improve it all by encapsulating and generalizing. Readysetgo.</p>
<h3 id="heading-encapsulate">Encapsulate</h3>
<p>The goal here is to only <code>import AcmeSupport</code> once in the app so that there's only one file that depends on it, and the rest can compile happily without it. To do this, let's create our <strong><em>own</em></strong> version of AcmeSupport's type <code>AcmeTicket</code> and we'll name ours something <em>generalized</em> like "<code>SupportTicket</code>" (foreshadowing, anyone? 😄)</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SupportTicket</span>: <span class="hljs-title">Identifiable</span> </span>{
    ...
}
</code></pre>
<p>Cool! Let's use our new type right away so you can see the effect it will have!</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> AcmeSupport

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AcmeSupportService</span> </span>{
    ...
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchAcmeTickets</span><span class="hljs-params">()</span></span> -&gt; async [<span class="hljs-type">SupportTicket</span>] { <span class="hljs-comment">// 👀</span>
        <span class="hljs-comment">// Call into the AcmeSupport API here</span>
        <span class="hljs-comment">// ✅ THEN map THEIR type to our OWN type to return 😃</span>
        ...
    }
}
</code></pre>
<p>Notice in the snippet that we're now returning an array of <code>SupportTicket</code> instead of AcmeSupport's type <code>AcmeTicket</code>. We do this by quickly mapping <strong><em>their</em></strong> type to our <strong><em>own</em></strong> type and walking away. This is really great, you know why? Because now all the other files in our app that were <em>previously</em> consuming an array of the <code>AcmeTicket</code> type will <em>now</em> be consuming an array of our very own <code>SupportTicket</code> type—which meeeeans all those files can immediately <strong><em>delete</em></strong><code>import AcmeSupport</code> because they won't need to access AcmeSupport's <code>AcmeTicket</code> type anymore! And just like that, we've successfully <em>encapsulated</em> the AcmeSupport dependency and its API calls into one single place in our app. Party time? ...Yeah. Party time. 🥳</p>
<h3 id="heading-generalize">Generalize</h3>
<p>Alright, in the original 3 code snippets, if you count the number of times the word "acme" appears in the code—excluding the <code>import</code> statements and the <code>AcmeTicket</code> type name—it comes out to around 15 times (and that's only in 3 tiny snippets representing 3 tiny files).</p>
<p>So the goal <em>now</em> is to <strong><em>generalize</em></strong> all of our naming (don't worry, I'm not talking about Swift generics 😅) so that it doesn't mention the name of the company, "Acme", anywhere. If we do this, the code instantly takes on a totally different feeling. Check it out:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> AcmeSupport <span class="hljs-comment">// ⬅️ imported only once, right here</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SupportService</span> </span>{
    ...
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchTickets</span><span class="hljs-params">()</span></span> -&gt; async [<span class="hljs-type">SupportTicket</span>] {
        ...
    }
}
</code></pre>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> Foundation

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataLogicPlace</span> </span>{
    ...
    <span class="hljs-keyword">let</span> service = <span class="hljs-type">SupportService</span>()
    ...
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loadTickets</span><span class="hljs-params">()</span></span> async -&gt; [<span class="hljs-type">SupportTicket</span>] {
        <span class="hljs-keyword">let</span> tickets = await service.fetchTickets()
        ...
    }
    ...
}
</code></pre>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SupportScreen</span>: <span class="hljs-title">View</span> </span>{
    ...
    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        ...
        <span class="hljs-type">ForEach</span>(tickets) { ticket <span class="hljs-keyword">in</span>
            rowView(forTicket: ticket)
        }
        ...
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">rowView</span><span class="hljs-params">(forTicket ticket: SupportTicket)</span></span> -&gt; some <span class="hljs-type">View</span> {
        ...
    }
}
</code></pre>
<p>And would you look a that! We've successfully <em>generalized</em> our naming, bringing the number of times we see "acme" in our naming from ~15 all the way down to <strong>zero</strong>! All without compromising the readability of the code. In fact, I'd say the readability has improved! Mission: accomplished.</p>
<h3 id="heading-is-this-really-necessary">Is this really necessary?</h3>
<p>You decide. Over the years, I've seen lots of code where devs tightly couple 3rd-party dependency names and their types to the rest of the codebase, making it unnecessarily intertwined and dependent on some other thing/company. And then the Product team asks the devs to a/b test 2 different dependencies/companies, or even completely <em>replace</em> a dependency with a different one that offers the same functionality as the original but perhaps at a better rate. Well, in these situations, if your code looks like the original 3 snippets in this article, then you'll have a lot of naming-refactoring ahead of you, and likely a lot of logic-refactoring, and also type-replacing, like in that original <code>acmeTicketRowView</code> method above that depended on the 3rd-party <code>AcmeTicket</code> type.</p>
<p>So if we thoughtfully encapsulate and generalize from the get-go, then our code can be a lot more pleasant to work with; scaling will be easier, and we'll be more likely to code more confidently when we need to do something drastic like <strong><em>completely replace</em></strong><code>AcmeSupport</code> with <code>BeepBeepSupport</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707810625709/00e4c44d-89ec-4aa9-ac13-82c57278ef79.gif" alt="Roadrunner cartoon character on a thin rock ledge jumping up while saying &quot;meep meep!&quot; then speeding away" class="image--center mx-auto" /></p>
<p><strong>"beep beep!"</strong></p>
<p>Come say "Hi!" on <a target="_blank" href="https://twitter.com/ScottSmithDev">Twitter</a> or <a target="_blank" href="https://mastodon.social/@ScottSmithDev">Mastodon</a>! Or don't. 😄</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707808245696/a0446bd5-7652-4391-a8ca-859273f842b8.gif" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Screen vs View in SwiftUI]]></title><description><![CDATA[When SwiftUI was announced in June 2019, I was on a team that decided we were going to use SwiftUI to build our new large-scale production app. We started right away and rode the wild roller coaster of the SwiftUI beta rush of Summer 2019, and we wer...]]></description><link>https://scottsmithdev.com/screen-vs-view-in-swiftui</link><guid isPermaLink="true">https://scottsmithdev.com/screen-vs-view-in-swiftui</guid><category><![CDATA[SwiftUI]]></category><dc:creator><![CDATA[Scott Smith]]></dc:creator><pubDate>Mon, 05 Feb 2024 16:45:32 GMT</pubDate><content:encoded><![CDATA[<p>When SwiftUI was announced in June 2019, I was on a team that decided we were going to use SwiftUI to build our new large-scale production app. We started right away and rode the wild roller coaster of the SwiftUI beta rush of Summer 2019, and we were loving it! Kudos to the SwiftUI team for working so feverishly that Summer! 🫶🏼</p>
<p>As we continued to build, the number of SwiftUI files naturally grew in our new project and it became <em>increasingly</em> difficult to tell whether a SwiftUI file name corresponded to the actual full layout of a screen or to a single custom view or component we had created. Because every single SwiftUI file we created ended in <code>View.swift</code>, and there were a <em>lot</em> of files! After all, literally no one in the world had ever built a large-scale Production app in this new "SwiftUI" framework, so we were forging our own path coming up with our own naming conventions. (I mean, "View" isn't anything novel, but hopefully you get what I mean.)</p>
<p>Then suddenly, a seemingly simple naming solution dawned on me: if you're creating a new SwiftUI file whose layout is intended to represent an entire screen, then use the <code>Screen</code> suffix, e.g. <code>SettingsScreen.swift</code>; otherwise, use the <code>View</code> suffix, e.g. <code>BasicRowView.swift</code>. And boom! Just like that—the moment we started using this naming convention, the struggle to distinguish between subview files and full-screen layout files vanished!</p>
<p>Fast forward almost 5 years (2024), I'm still working full-time using SwiftUI, and I still follow the exact same naming convention. But something has changed. In those early days, we were extraction-happy: we extracted subviews out into their own files all over the place. And that's how we ended up with a <em>lot</em> of files. I'm not saying that's a bad thing though. The thing that has changed/evolved since then is my <em>approach</em> to view extraction. These days, the vast majority of my SwiftUI files are <code>Screen</code> files, with only <em>some</em><code>View</code> files. Because I've become <em>much</em> more selective about what I extract out into its own <code>View</code> file. I'll explain.</p>
<p>Let's say your app has several screens that each list things out, and every screen uses the <em>exact</em> same UI for the rows. In this situation, since the row layout is used in <strong><em>more than one</em></strong> screen in the app, I would extract the row UI (which I would consider a "subview" of a "screen") into its own file using the <code>View</code> suffix, e.g. <code>PrimaryRowView.swift</code> and I would use it throughout the app. On the flip side, you might have a screen that uses a unique style for its rows and that particular screen is <strong>the <em>only</em> screen</strong> in the app that uses that unique style of row. Well, since that row style is only used in one single screen in the app, I would opt to create the row layout directly within the <code>Screen</code> file itself using a <code>func</code> (and/or computed property) returning <code>some View</code> like this:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">cardRowView</span><span class="hljs-params">(title: String)</span></span> -&gt; some <span class="hljs-type">View</span> {
    ...
}
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Notice how I used the "<code>View</code>" suffix in the <code>func</code> name since I consider this is a "subview" of the "screen" that it lives in.</div>
</div>

<p>If, at any point in time, that unique style ends up being used in more that one screen, then it's a pretty light lift to bust it out into its own file as a reusable component. No big deal.</p>
<p>It may be worth mentioning, <a target="_blank" href="https://x.com/Dimillian/status/1735226258269569295?s=20">some say</a> breaking up <strong><em>complex</em></strong> views into smaller pieces that live in their own files may improve performance. I haven't measured this sort of thing myself, and my article today isn't about performance. Full stop 😅</p>
<p>So, hey, you've read an article about a naming convention. And it probably won't change the world. But it might change <strong><em>your</em></strong> world by making your code <em>vastly</em> easier to reason about. And if your code is easier to reason about, it'll be that much easier for <strong><em>you</em></strong> to change the world. ...what? Go to sleep.</p>
<p>That was way too serious.<br />kloveyoubye 👋🏼</p>
<p>P.S. Do you remember what it was like Googling something about SwiftUI in Summer 2019 and having it return 0 results? 😅</p>
]]></content:encoded></item><item><title><![CDATA[An Approach to Handling App Launch States in SwiftUI]]></title><description><![CDATA[Let's explore how we can show specific Views for different launch states while your SwiftUI app is opening & loading up. Giving users an awesome experience every time, and making your code super easy to maintain and follow. I have a deep love for enu...]]></description><link>https://scottsmithdev.com/an-approach-to-handling-app-launch-states-in-swiftui</link><guid isPermaLink="true">https://scottsmithdev.com/an-approach-to-handling-app-launch-states-in-swiftui</guid><category><![CDATA[Swift]]></category><category><![CDATA[SwiftUI]]></category><dc:creator><![CDATA[Scott Smith]]></dc:creator><pubDate>Mon, 22 Jan 2024 07:00:08 GMT</pubDate><content:encoded><![CDATA[<p>Let's explore how we can show specific <code>View</code>s for different launch states while your SwiftUI app is opening &amp; loading up. Giving users an awesome experience every time, and making your code super easy to maintain and follow. I have a <a target="_blank" href="https://x.com/ScottSmithDev/status/1745155898677604683?s=20">deep love</a> for <code>enum</code>s, so we'll base this approach all on an <code>enum</code> 😍. Let's go!</p>
<p>We can start by creating an <code>@Observable</code> class as a place to ultimately hold &amp; update the current state of the app as it launches and as we're preparing to show the correct screen to the user. For this demo, let's name the class <code>AppLauncher</code> (I dunno, naming is tricky. Go with it. 😆)</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI

@<span class="hljs-type">Observable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppLauncher</span> </span>{

}
</code></pre>
<p>Cool. Now, let's make use of my best friend, <code>enum</code>, to describe the different launch states we need to handle.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI

@<span class="hljs-type">Observable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppLauncher</span> </span>{
    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">LaunchState</span> </span>{
        <span class="hljs-keyword">case</span> loading
        <span class="hljs-keyword">case</span> pendingAuth
        <span class="hljs-keyword">case</span> doneWithNothingPending
    }

    <span class="hljs-keyword">var</span> launchState = <span class="hljs-type">LaunchState</span>.loading

    <span class="hljs-comment">// We'll do stuff right here in a minute...keep reading</span>
}
</code></pre>
<p>Great! This is shaping up nicely! And since our class is <code>@Observable</code> we're already prepared to hook up the <code>launchState</code> variable to our <code>@mainApp</code> struct and start observing its changes! So let's do that now by, first, heading over to the <code>App</code> struct and adding a <code>@State</code> instance of <code>AppLauncher</code>. (And I'll name our demo app <code>Milkshakes</code> because I love milkshakes.)</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI

@main
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Milkshakes</span>: <span class="hljs-title">App</span> </span>{

    @<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> launcher = <span class="hljs-type">AppLauncher</span>()

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">Scene</span> {
        <span class="hljs-type">WindowGroup</span> {
            <span class="hljs-type">Text</span>(<span class="hljs-string">"We'll do something right here next!"</span>)
        }
    }
}
</code></pre>
<p>Real quick, in order to keep the <code>body</code> property as concise as possible, we'll move the <code>Text</code> into a new <code>bodyContentView</code> method (passing in the <code>launchState</code> for later) and return it from there instead.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI

@main
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Milkshakes</span>: <span class="hljs-title">App</span> </span>{

    @<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> launcher = <span class="hljs-type">AppLauncher</span>()

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">Scene</span> {
        <span class="hljs-type">WindowGroup</span> {
            bodyContentView(launchState: launcher.launchState)
        }
    }

    @<span class="hljs-type">ViewBuilder</span> 
    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">bodyContentView</span><span class="hljs-params">(launchState: AppLauncher.LaunchState)</span></span> -&gt; some <span class="hljs-type">View</span> {
        <span class="hljs-comment">// Get ready to rock!</span>
        <span class="hljs-type">Text</span>(<span class="hljs-string">"This is where things get really exciting!"</span>)
    }
}
</code></pre>
<p>Alright, we're finally ready to rock! Let's spread the butter on this <code>launchState</code> bread using <code>enum</code>'s partner in crime, the <code>switch</code> statement 🎉 (my <em>other</em> best friend). We'll delete the <code>Text</code> in the method and start typing <code>switch launchState</code> － and it's in this exact moment where we leverage Xcode's awesome auto-complete to get this output:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">switch</span> launchState {
<span class="hljs-keyword">case</span> .loading:

<span class="hljs-keyword">case</span> .pendingAuth:

<span class="hljs-keyword">case</span> .doneWithNothingPending:

}
</code></pre>
<p>Love it! Exhaustivity for the win! Oh, and did you notice that I put <code>@ViewBuilder</code> on the <code>bodyContentView(launchState:)</code> method earlier? That sets us up <em>perfectly</em> to return a <strong><em>unique</em></strong><code>View</code> in each <code>case</code> of the <code>enum</code>! 😍 So what are we waiting for?!</p>
<pre><code class="lang-swift">@<span class="hljs-type">ViewBuilder</span> 
<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">bodyContentView</span><span class="hljs-params">(launchState: AppLauncher.LaunchState)</span></span> -&gt; some <span class="hljs-type">View</span> {
    <span class="hljs-keyword">switch</span> launchState {
    <span class="hljs-keyword">case</span> .loading:
        <span class="hljs-type">AppLaunchLoadingScreen</span>()

    <span class="hljs-keyword">case</span> .pendingAuth:
        <span class="hljs-type">AppIntroScreen</span>()

    <span class="hljs-keyword">case</span> .doneWithNothingPending:
        <span class="hljs-type">AppRootTabView</span>()
    }
}
</code></pre>
<p>Boom, baby! With all this in place, our Milkshakes app successfully observes any changes to <code>launchState</code> and shows the appropriate <code>View</code> for each state! 🥳</p>
<p>Hold on though..! 😅 We still need to tell <code>AppLauncher</code> to start doing stuff so it can actually change the value of <code>launchState</code> from the default <code>.loading</code> state to something else. One way to do that is to create a method in <code>AppLauncher</code> to call from the <code>App</code> struct in an <code>.onAppear</code> modifier. Let's see what that looks like real quick.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI

@<span class="hljs-type">Observable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppLauncher</span> </span>{
    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">LaunchState</span> </span>{
        <span class="hljs-keyword">case</span> loading
        <span class="hljs-keyword">case</span> pendingAuth
        <span class="hljs-keyword">case</span> doneWithNothingPending
    }

    <span class="hljs-keyword">var</span> launchState = <span class="hljs-type">LaunchState</span>.loading

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">load</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Do whatever setup code you want</span>
        ...

        <span class="hljs-comment">// Update the launch state accordingly</span>
        launchState = .doneWithNothingPending
    }
}
</code></pre>
<p>Meanwhile, in the <code>App</code> struct, we call the new <code>load</code> method in <code>.onAppear</code>:</p>
<pre><code class="lang-swift"><span class="hljs-type">WindowGroup</span> {
    bodyContentView(launchState: launcher.launchState)
        .onAppear { launcher.load() }
}
</code></pre>
<p>And that's it! We're officially done!</p>
<p>If you're thirsty for a bonus addition to this code, keep reading. Otherwise, thank you for sticking around and I hope you found some value here!<br />K love you bye 👋🏽</p>
<h3 id="heading-animate-the-state-changes">Animate the State Changes</h3>
<p>Consider slightly animating the <code>launchState</code> changes by adding an <code>.animation</code> modifier right under <code>.onAppear</code>. 👌🏼</p>
<pre><code class="lang-swift">.onAppear { launcher.load() }
.animation(.<span class="hljs-keyword">default</span>, value: launcher.launchState)
</code></pre>
]]></content:encoded></item><item><title><![CDATA[It's me. Hi. I'm starting a blog, it's me.]]></title><description><![CDATA[Hi! 😃 I'm Scott, and I'm finally starting a blog! It's probably no surprise that it'll be geared around iOS/Apple platform development (Swift, SwiftUI, and more). The iOS dev community is more than outstanding and I've loved contributing to it and c...]]></description><link>https://scottsmithdev.com/first</link><guid isPermaLink="true">https://scottsmithdev.com/first</guid><dc:creator><![CDATA[Scott Smith]]></dc:creator><pubDate>Thu, 11 Jan 2024 07:09:37 GMT</pubDate><content:encoded><![CDATA[<p>Hi! 😃 I'm <a target="_blank" href="https://twitter.com/ScottSmithDev">Scott</a>, and I'm finally starting a blog! It's probably no surprise that it'll be geared around iOS/Apple platform development (Swift, SwiftUI, and more). The iOS dev community is <em>more</em> than outstanding and I've loved contributing to it and cultivating relationships through it, so I'm excited to have another place to share my thoughts around it. I really hope you find something useful, valuable, and motivating here along the way! Now, if you'd excuse me, I'm about to go eat some delicious ice cream pie. K love you bye! 👋🏼</p>
<p>P.S. I <strong><em>really</em></strong> love 🩷 enums in Swift!</p>
]]></content:encoded></item></channel></rss>