<?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[Dev Faganello]]></title><description><![CDATA[Dev Faganello]]></description><link>https://blog.faganello.dev.br</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1718476727378/Bm1Vfx15x.png</url><title>Dev Faganello</title><link>https://blog.faganello.dev.br</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 02:14:18 GMT</lastBuildDate><atom:link href="https://blog.faganello.dev.br/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Use ContextMenu in SwiftUI]]></title><description><![CDATA[https://youtu.be/y4sotY_fESs
 
One of the most effective ways to improve the user experience of an iOS app is by utilizing features that feel native and familiar to the user. A perfect example of this is the Context Menu.
You are likely already famil...]]></description><link>https://blog.faganello.dev.br/how-to-use-contextmenu-in-swiftui</link><guid isPermaLink="true">https://blog.faganello.dev.br/how-to-use-contextmenu-in-swiftui</guid><category><![CDATA[iOS]]></category><category><![CDATA[SwiftUI]]></category><category><![CDATA[components]]></category><category><![CDATA[ios app development]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Thu, 27 Nov 2025 14:45:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1764202708589/241b54e1-b09a-491c-a164-f6a918c191d4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/y4sotY_fESs">https://youtu.be/y4sotY_fESs</a></div>
<p> </p>
<p>One of the most effective ways to improve the user experience of an iOS app is by utilizing features that feel native and familiar to the user. A perfect example of this is the <strong>Context Menu</strong>.</p>
<p>You are likely already familiar with this interaction: when you long-press an app icon on your iPhone’s home screen, a menu pops up offering shortcuts like "Rename," "Edit Home Screen," or "Remove App." Despite being a standard iOS behavior that users expect, many developers forget to implement it in their own applications.</p>
<p>The good news is that implementing Context Menus in SwiftUI is incredibly simple and provides significant value with very little code.</p>
<h3 id="heading-how-it-works">How It Works</h3>
<p>A Context Menu is triggered by a long-press gesture. When activated, the system automatically handles the complex UI transitions for you: it blurs the background, brings the selected view into focus (often with a slight zoom), and displays a list of actions.</p>
<h3 id="heading-implementation">Implementation</h3>
<p>To add this feature, you simply use the <code>.contextMenu</code> modifier on any view. This modifier accepts a <code>ViewBuilder</code> closure where you can define the contents of the menu.</p>
<p>While you can technically place various types of views inside the menu (like a static <code>Text</code> view for information), the best practice is to use buttons to provide actionable items.</p>
<h3 id="heading-dynamic-content">Dynamic Content</h3>
<p>You can also make the menu dynamic. In the example provided below, we use a <code>@State</code> variable to track whether an item has been "liked." The menu icon changes between a hollow heart and a filled heart depending on that state, giving the user immediate visual feedback the next time they open the menu.</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ContentView</span>: <span class="hljs-title">View</span> </span>{
    <span class="hljs-comment">// 1. State variable to track the 'liked' status</span>
    @<span class="hljs-type">State</span> <span class="hljs-keyword">var</span> liked: <span class="hljs-type">Bool</span> = <span class="hljs-literal">false</span>

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        <span class="hljs-type">Image</span>(<span class="hljs-string">"Image"</span>) <span class="hljs-comment">// Replace with your image asset name</span>
            .resizable()
            .frame(width: <span class="hljs-number">300</span>, height: <span class="hljs-number">200</span>)
            <span class="hljs-comment">// 2. The Context Menu Modifier</span>
            .contextMenu {

                <span class="hljs-comment">// You can add static text (informational)</span>
                <span class="hljs-type">Text</span>(<span class="hljs-string">"Information"</span>)

                <span class="hljs-comment">// 3. Action Button: Toggles the state</span>
                <span class="hljs-type">Button</span> {
                    liked.toggle()
                } label: {
                    <span class="hljs-comment">// Logic to swap the icon based on state</span>
                    <span class="hljs-type">Label</span>(<span class="hljs-string">"Like"</span>, systemImage: liked ? <span class="hljs-string">"heart.fill"</span> : <span class="hljs-string">"heart"</span>)
                }

                <span class="hljs-comment">// 4. Standard Action Button</span>
                <span class="hljs-type">Button</span> {
                    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Delete action triggered"</span>)
                } label: {
                    <span class="hljs-type">Label</span>(<span class="hljs-string">"Trash"</span>, systemImage: <span class="hljs-string">"trash"</span>)
                }
            }
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764202825015/ad73b56d-1ca7-47b2-8335-23eaee5913fa.png" alt /></p>
]]></content:encoded></item><item><title><![CDATA[Swift Tip: A Cleaner Way to Handle Optional Defaults in Strings]]></title><description><![CDATA[https://youtu.be/L0X1BK4D6mc
 
Handling Optionals is a bread-and-butter task for Swift developers. We all know the standard way to unwrap a value or provide a fallback, but things get tricky when we are dealing with String interpolation and data type...]]></description><link>https://blog.faganello.dev.br/swift-tip-a-cleaner-way-to-handle-optional-defaults-in-strings</link><guid isPermaLink="true">https://blog.faganello.dev.br/swift-tip-a-cleaner-way-to-handle-optional-defaults-in-strings</guid><category><![CDATA[Swift]]></category><category><![CDATA[tips]]></category><category><![CDATA[iOS]]></category><category><![CDATA[Optional chaining]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Fri, 21 Nov 2025 14:45:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763654734407/429c52ca-f75e-416b-88dd-0c976dfe111e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/L0X1BK4D6mc">https://youtu.be/L0X1BK4D6mc</a></div>
<p> </p>
<p>Handling <strong>Optionals</strong> is a bread-and-butter task for Swift developers. We all know the standard way to unwrap a value or provide a fallback, but things get tricky when we are dealing with String interpolation and data types that don't match (like Integers vs Strings).</p>
<p>In this post, we’ll look at a common friction point when printing optional values and a cleaner syntax (introduced in newer Swift versions) to handle default values without messy type-casting.</p>
<h2 id="heading-the-standard-approach-nil-coalescing">The Standard Approach: Nil-Coalescing</h2>
<p>The most common way to handle a nil value inside a print statement is using the <strong>Nil-Coalescing Operator (</strong><code>??</code>).</p>
<p>If you have a generic string optional, this works perfectly.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">var</span> name: <span class="hljs-type">String?</span> = <span class="hljs-literal">nil</span>

<span class="hljs-comment">// Standard coalescing works because both sides are Strings</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Hello, \(name ?? "</span><span class="hljs-type">Unknown</span><span class="hljs-string">")"</span>) 

<span class="hljs-comment">// Output: Hello, Unknown</span>
</code></pre>
<p>If <code>name</code> has a value, it prints the name. If it is <code>nil</code>, it falls back to "Unknown". Simple, right?</p>
<h2 id="heading-the-problem-type-mismatch">The Problem: Type Mismatch</h2>
<p>The issue arises when the optional variable is <strong>not</strong> a String, but you want the default fallback to be text.</p>
<p>Let's say you are tracking a user's age. If the age is known, you want to print the number. If it is unknown (nil), you want to print "Unknown".</p>
<p><strong>Attempting to use</strong> <code>??</code> will cause a crash or compiler error here:</p>
<p>Swift</p>
<pre><code class="lang-swift"><span class="hljs-keyword">var</span> age: <span class="hljs-type">Int?</span> = <span class="hljs-literal">nil</span>

<span class="hljs-comment">// ❌ Error: Cannot convert value of type 'Int?' to expected argument type 'String'</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Age: \(age ?? "</span><span class="hljs-type">Unknown</span><span class="hljs-string">")"</span>)
</code></pre>
<h3 id="heading-why-does-this-fail">Why does this fail?</h3>
<p>The <code>??</code> operator expects both the left side (<code>Int?</code>) and the right side (<code>String</code>) to be of compatible types. Swift cannot magically decide that the result should be a String just because you are inside a print statement.</p>
<h3 id="heading-the-old-workaround">The Old Workaround</h3>
<p>Previously, developers might force the default value to match the type, resulting in bad semantics. For example, defaulting to <code>0</code>:</p>
<p>Swift</p>
<pre><code class="lang-swift"><span class="hljs-built_in">print</span>(<span class="hljs-string">"Age: \(age ?? 0)"</span>)
<span class="hljs-comment">// Output: Age: 0</span>
</code></pre>
<p>This is technically correct code, but logically incorrect. A user with an unknown age is not a newborn baby (0 years old).</p>
<h2 id="heading-the-solution-interpolation-with-default">The Solution: Interpolation with Default</h2>
<p>There is a much cleaner way to handle this directly inside String Interpolation without needing to convert types manually or use complex <code>if-let</code> statements.</p>
<p>You can use the <code>default:</code> parameter directly within the interpolation structure.</p>
<p>Here is the updated, clean code:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">var</span> age: <span class="hljs-type">Int?</span> = <span class="hljs-literal">nil</span>

<span class="hljs-comment">// ✅ The Clean Way</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Age: \(age, default: "</span><span class="hljs-type">Unknown</span><span class="hljs-string">")"</span>)
</code></pre>
<h3 id="heading-how-it-works">How it works:</h3>
<ol>
<li><p><strong>If</strong> <code>age</code> is nil: Swift uses the value provided in the <code>default</code> parameter. It automatically handles the fact that "Unknown" is a String, allowing the print statement to compile successfully.</p>
</li>
<li><p><strong>If</strong> <code>age</code> has a value (e.g., 25): It unwraps the Integer and prints "25".</p>
</li>
</ol>
<pre><code class="lang-swift"><span class="hljs-comment">// Example with a value</span>
age = <span class="hljs-number">25</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Age: \(age, default: "</span><span class="hljs-type">Unknown</span><span class="hljs-string">")"</span>)

<span class="hljs-comment">// Output: Age: 25</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Mastering Resizable Layouts with SwiftUI's SplitView Components ( HSplitView & VSplitView)]]></title><description><![CDATA[https://youtu.be/nKNSYYXHTCY
 
SwiftUI offers two primary components for creating resizable divisions:
•HSplitView: Used to arrange views horizontally, separated by a vertical, draggable divider. This is ideal for side-by-side content areas, such as ...]]></description><link>https://blog.faganello.dev.br/mastering-resizable-layouts-with-swiftuis-splitview-components-hsplitview-and-vsplitview</link><guid isPermaLink="true">https://blog.faganello.dev.br/mastering-resizable-layouts-with-swiftuis-splitview-components-hsplitview-and-vsplitview</guid><category><![CDATA[SwiftUI]]></category><category><![CDATA[components]]></category><category><![CDATA[macOS]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Mon, 17 Nov 2025 23:19:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763421499973/754c0ec3-1341-45cd-a32e-addb0a94c907.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/nKNSYYXHTCY">https://youtu.be/nKNSYYXHTCY</a></div>
<p> </p>
<p>SwiftUI offers two primary components for creating resizable divisions:</p>
<p>•HSplitView: Used to arrange views horizontally, separated by a vertical, draggable divider. This is ideal for side-by-side content areas, such as a sidebar and a main content pane.</p>
<p>•VSplitView: Used to arrange views vertically, separated by a horizontal, draggable divider. This is useful for dividing a single content area into top and bottom sections.</p>
<p>The fundamental benefit of these components is that they automatically handle the user interaction required for resizing. By simply embedding views within an HSplitView or VSplitView, a developer instantly gains a fully functional, resizable divider.</p>
<h2 id="heading-basic-implementation">Basic Implementation:</h2>
<p>A simple implementation of a resizable interface involves using HSplitView to divide the application window into two distinct, adjustable sections.</p>
<p>For example, to create a layout with a blue panel on the left and a purple panel on the right, the structure is straightforward:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ContentView</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">HSplitView</span> {
            <span class="hljs-comment">// Left Panel</span>
            <span class="hljs-type">Color</span>.blue
                .frame(minWidth: <span class="hljs-number">100</span>)

            <span class="hljs-comment">// Right Panel</span>
            <span class="hljs-type">Color</span>.purple
                .frame(minWidth: <span class="hljs-number">100</span>)
        }
    }
}
</code></pre>
<p>When the application runs, the user can click and drag the vertical divider between the blue and purple panels to resize them, and the views will automatically adapt to the new dimensions.</p>
<h2 id="heading-advanced-layouts">Advanced Layouts</h2>
<p>For more complex application designs, HSplitView and VSplitView can be nested to create multi-dimensional, resizable layouts. This technique allows for both horizontal and vertical resizing within the same window.</p>
<p>Consider a scenario where the right-hand side of the horizontal split needs to be further divided vertically into a red and a green section. This is achieved by placing a VSplitView inside the right-hand pane of the HSplitView:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ComplexContentView</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">HSplitView</span> {
            <span class="hljs-comment">// Left Panel (Blue)</span>
            <span class="hljs-type">Color</span>.blue
                .frame(minWidth: <span class="hljs-number">100</span>)

            <span class="hljs-comment">// Right Panel (Vertically Split)</span>
            <span class="hljs-type">VSplitView</span> {
                <span class="hljs-comment">// Top-Right Panel</span>
                <span class="hljs-type">Color</span>.red
                    .frame(minHeight: <span class="hljs-number">50</span>)

                <span class="hljs-comment">// Bottom-Right Panel</span>
                <span class="hljs-type">Color</span>.green
                    .frame(minHeight: <span class="hljs-number">50</span>)
            }
        }
    }
}
</code></pre>
<h2 id="heading-controlling-resizing-with-constraints">Controlling Resizing with Constraints</h2>
<p>While the default behavior allows for free resizing, developers often need to impose limits to maintain a usable interface. This is accomplished using standard SwiftUI frame modifiers.</p>
<p>By applying modifiers like .frame(maxWidth: 200) to a view within a split view, you can set a maximum size for that pane. The divider will stop moving once the pane reaches this constraint, ensuring that critical content areas are not completely obscured or resized beyond a functional limit.</p>
<p>For instance, to ensure the left panel never exceeds 200 points in width:</p>
<pre><code class="lang-swift"><span class="hljs-type">HSplitView</span> {
    <span class="hljs-type">Color</span>.blue
        .frame(minWidth: <span class="hljs-number">100</span>, maxWidth: <span class="hljs-number">200</span>) <span class="hljs-comment">// Constrained Panel</span>

    <span class="hljs-type">Color</span>.purple
        .frame(minWidth: <span class="hljs-number">100</span>) <span class="hljs-comment">// Flexible Panel</span>
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Parallelism in Swift Concurrency with async let]]></title><description><![CDATA[https://www.youtube.com/watch?v=vRT_vNmoRQU
 
The Bottleneck of Sequential await
In Swift's structured concurrency, the await keyword is used to pause the execution of the current task until the asynchronous function it calls returns a result. While ...]]></description><link>https://blog.faganello.dev.br/parallelism-in-swift-concurrency-with-async-let</link><guid isPermaLink="true">https://blog.faganello.dev.br/parallelism-in-swift-concurrency-with-async-let</guid><category><![CDATA[Swift]]></category><category><![CDATA[concurrency]]></category><category><![CDATA[parallelism]]></category><category><![CDATA[asynchronous]]></category><category><![CDATA[SwiftUI]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Mon, 17 Nov 2025 14:55:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763391288301/be693e39-3d7c-4ce8-9f74-c043b4528d5c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=vRT_vNmoRQU">https://www.youtube.com/watch?v=vRT_vNmoRQU</a></div>
<p> </p>
<h2 id="heading-the-bottleneck-of-sequential-await">The Bottleneck of Sequential <code>await</code></h2>
<p>In Swift's structured concurrency, the await keyword is used to pause the execution of the current task until the asynchronous function it calls returns a result. While this is essential for managing dependencies, using multiple sequential await calls for tasks that do not depend on each other forces the program to wait for each operation to complete before starting the next.</p>
<p>Consider a scenario where an application needs to fetch six independent pieces of data (simulated here by six network requests). The sequential approach looks like this:</p>
<pre><code class="lang-swift"><span class="hljs-type">Task</span> { <span class="hljs-keyword">let</span> startInstant = clock.now
    <span class="hljs-comment">// Each 'await' pauses execution until the previous one is complete</span>
    <span class="hljs-keyword">let</span> joke1 = <span class="hljs-keyword">try</span>! await requestJoke(index: <span class="hljs-number">1</span>)
    <span class="hljs-keyword">let</span> joke2 = <span class="hljs-keyword">try</span>! await requestJoke(index: <span class="hljs-number">2</span>)
    <span class="hljs-keyword">let</span> joke3 = <span class="hljs-keyword">try</span>! await requestJoke(index: <span class="hljs-number">3</span>)
    <span class="hljs-keyword">let</span> joke4 = <span class="hljs-keyword">try</span>! await requestJoke(index: <span class="hljs-number">4</span>)
    <span class="hljs-keyword">let</span> joke5 = <span class="hljs-keyword">try</span>! await requestJoke(index: <span class="hljs-number">5</span>)
    <span class="hljs-keyword">let</span> joke6 = <span class="hljs-keyword">try</span>! await requestJoke(index: <span class="hljs-number">6</span>)

    jokes = [joke1, joke2, joke3, joke4, joke5, joke6]
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Time taken = \(clock.now - startInstant)"</span>)
}

<span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">requestJoke</span><span class="hljs-params">(index: Int)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">String</span> { 
    <span class="hljs-keyword">let</span> url = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://api.chucknorris.io/jokes/random"</span> )! 
    <span class="hljs-keyword">let</span> (data, <span class="hljs-number">_</span>) = <span class="hljs-keyword">try</span> await <span class="hljs-type">URLSession</span>.shared.data(from: url)
    <span class="hljs-keyword">let</span> decoded = <span class="hljs-keyword">try</span> <span class="hljs-type">JSONSerialization</span>.jsonObject(with: data, options: []) <span class="hljs-built_in">print</span>(<span class="hljs-string">"(index)"</span>) 
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> dict = decoded <span class="hljs-keyword">as</span>? [<span class="hljs-type">String</span>: <span class="hljs-type">Any</span>] { 
        <span class="hljs-keyword">return</span> dict[<span class="hljs-string">"value"</span>] <span class="hljs-keyword">as</span>! <span class="hljs-type">String</span> 
    } 
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Error"</span> 
}
</code></pre>
<p>In a test environment, this sequential execution took approximately 2.2 to 2.3 seconds to complete all six requests. The total time is roughly the sum of the individual request times, as they run one after the other.</p>
<h2 id="heading-achieving-true-parallelism-with-async-let">Achieving True Parallelism with async let</h2>
<p>The async let binding is designed to solve this exact problem. It allows you to declare an asynchronous value, immediately starting the task in parallel with the current execution flow. The task runs concurrently in the background, and the current task continues without pausing. You only use await later when you actually need the result of that task.</p>
<p>By using async let, all six network requests are initiated almost simultaneously, and the program only pauses at the final step to collect the results.</p>
<pre><code class="lang-swift"><span class="hljs-type">Task</span> { <span class="hljs-keyword">let</span> startInstant = clock.now
    <span class="hljs-comment">// Each 'await' pauses execution until the previous one is complete</span>
    async <span class="hljs-keyword">let</span> joke1 = <span class="hljs-keyword">try</span>! requestJoke(index: <span class="hljs-number">1</span>)
    async <span class="hljs-keyword">let</span> joke2 = <span class="hljs-keyword">try</span>! requestJoke(index: <span class="hljs-number">2</span>)
    async <span class="hljs-keyword">let</span> joke3 = <span class="hljs-keyword">try</span>! requestJoke(index: <span class="hljs-number">3</span>)
    async <span class="hljs-keyword">let</span> joke4 = <span class="hljs-keyword">try</span>! requestJoke(index: <span class="hljs-number">4</span>)
    async <span class="hljs-keyword">let</span> joke5 = <span class="hljs-keyword">try</span>! requestJoke(index: <span class="hljs-number">5</span>)
    async <span class="hljs-keyword">let</span> joke6 = <span class="hljs-keyword">try</span>! requestJoke(index: <span class="hljs-number">6</span>)

    <span class="hljs-comment">// The 'await' here waits for all six concurrent tasks to finish</span>
    jokes = await [<span class="hljs-keyword">try</span>! joke1, <span class="hljs-keyword">try</span>! joke2, <span class="hljs-keyword">try</span>! joke3, <span class="hljs-keyword">try</span>! joke4, <span class="hljs-keyword">try</span>! joke5, <span class="hljs-keyword">try</span>! joke6]
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Time taken = \(clock.now - startInstant)"</span>)
}
</code></pre>
<p>The performance improvement is dramatic. In the same test environment, the parallel execution using async let completed in approximately 1.0 to 1.1 seconds. This is a reduction of over 50% in execution time, as the total time is now dictated by the longest-running task, not the sum of all tasks.</p>
<h2 id="heading-summary-of-performance">Summary of Performance</h2>
<p>The following table summarizes the performance difference between the two approaches:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Execution Method</td><td>Task Initiation</td><td>Total Execution Time (Approx.)</td><td>Performance Gain</td></tr>
</thead>
<tbody>
<tr>
<td>Sequential await</td><td>One task starts after the previous one finishes.</td><td>2.2 - 2.3 seconds</td><td>Baseline</td></tr>
<tr>
<td>Parallel async let</td><td>All tasks start concurrently.</td><td>1.0 - 1.1 seconds</td><td>~55% Faster</td></tr>
</tbody>
</table>
</div><h2 id="heading-conclusion">Conclusion</h2>
<p>For any Swift application that needs to perform multiple independent asynchronous operations, adopting async let is a straightforward and highly effective way to leverage the power of structured concurrency and achieve significant performance gains. It provides a clean, readable, and modern alternative to older techniques like DispatchGroup, ensuring your application remains responsive and efficient.</p>
<p>By replacing sequential await calls with async let for independent tasks, you can ensure that your application is utilizing system resources to their fullest, leading to a faster and more fluid user experience.</p>
]]></content:encoded></item><item><title><![CDATA[The Essential Difference Between .task and .onAppear in SwiftUI]]></title><description><![CDATA[https://www.youtube.com/watch?v=CiObtyFFuSs
 

The video is in Portuguese but with English subtitles

This article explores the critical distinction between the .task and .onAppear view modifiers in SwiftUI, a common point of confusion and a frequent...]]></description><link>https://blog.faganello.dev.br/the-essential-difference-between-task-and-onappear-in-swiftui</link><guid isPermaLink="true">https://blog.faganello.dev.br/the-essential-difference-between-task-and-onappear-in-swiftui</guid><category><![CDATA[Swift]]></category><category><![CDATA[SwiftUI]]></category><category><![CDATA[iOS]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Sun, 16 Nov 2025 18:09:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763316442263/10697e67-6ef3-4bbd-875e-69a53a8efc23.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=CiObtyFFuSs">https://www.youtube.com/watch?v=CiObtyFFuSs</a></div>
<p> </p>
<blockquote>
<p><strong><em>The video is in Portuguese but with English subtitles</em></strong></p>
</blockquote>
<p>This article explores the critical distinction between the .task and .onAppear view modifiers in SwiftUI, a common point of confusion and a frequent topic in iOS development interviews. While both modifiers execute code when a view is presented, their fundamental difference lies in their handling of asynchronous operations and automatic task cancellation.</p>
<h2 id="heading-1-execution-timing-a-minor-difference">1. Execution Timing: A Minor Difference</h2>
<p>Both .onAppear and .task are triggered when the view is about to be displayed on the screen. The video demonstrates that .onAppear runs marginally before .task, but for most practical purposes, they are both executed at the view's presentation.</p>
<h3 id="heading-example-1-synchronous-code">Example 1: Synchronous Code</h3>
<p>For synchronous operations, both modifiers function identically.</p>
<p>Conceptual Code Example (Synchronous):</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ContentView</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">Text</span>(<span class="hljs-string">"Hello, World!"</span>)
            .onAppear {
                <span class="hljs-comment">// Synchronous code runs successfully</span>
                startAnimation()
            }
            .task {
                <span class="hljs-comment">// Synchronous code runs successfully</span>
                startAnimation()
            }
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">startAnimation</span><span class="hljs-params">()</span></span> {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"Starting UI animation..."</span>)
        <span class="hljs-comment">// ... animation logic ...</span>
    }
}
</code></pre>
<h2 id="heading-2-the-core-distinction-asynchronous-operations">2. The Core Distinction: Asynchronous Operations</h2>
<p>The true difference emerges when dealing with asynchronous code, such as network requests or long-running background tasks.</p>
<h3 id="heading-example-2-asynchronous-code">Example 2: Asynchronous Code</h3>
<h4 id="heading-the-onappear-limitation">The .onAppear Limitation</h4>
<p>The closure provided to .onAppear is synchronous. Attempting to call an async function directly using await will result in a compilation error.</p>
<p>Conceptual Code Example (Error):</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ContentView</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">Text</span>(<span class="hljs-string">"Hello, World!"</span>)
            .onAppear {
                <span class="hljs-comment">// ❌ ERROR: 'async' call in a function that does not support concurrency</span>
                await fetchData()
            }
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchData</span><span class="hljs-params">()</span></span> async -&gt; <span class="hljs-type">Data</span> {
        <span class="hljs-comment">// ... network request logic ...</span>
        <span class="hljs-keyword">return</span> <span class="hljs-type">Data</span>()
    }
}
</code></pre>
<h4 id="heading-the-onappear-workaround">The .onAppear Workaround</h4>
<p>To execute asynchronous code within .onAppear, you must manually wrap the call in a Task block.</p>
<p>Conceptual Code Example (Workaround):</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ContentView</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">Text</span>(<span class="hljs-string">"Hello, World!"</span>)
            .onAppear {
                <span class="hljs-type">Task</span> {
                    <span class="hljs-comment">// Manual Task wrapper required</span>
                    <span class="hljs-keyword">let</span> data = await fetchData()
                    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Data fetched via onAppear workaround: \(data.count) bytes"</span>)
                }
            }
    }
    <span class="hljs-comment">// ... fetchData() async function ...</span>
}
</code></pre>
<h4 id="heading-the-task-solution">The .task Solution</h4>
<p>The .task modifier is designed specifically for concurrency. Its closure is implicitly asynchronous, allowing for the direct use of await without any manual wrapping.</p>
<p>Conceptual Code Example (Recommended):</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ContentView</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">Text</span>(<span class="hljs-string">"Hello, World!"</span>)
            .task {
                <span class="hljs-comment">// ✅ Direct use of await is supported</span>
                <span class="hljs-keyword">let</span> data = await fetchData()
                <span class="hljs-built_in">print</span>(<span class="hljs-string">"Data fetched via task: \(data.count) bytes"</span>)
            }
    }
    <span class="hljs-comment">// ... fetchData() async function ...</span>
}
</code></pre>
<h2 id="heading-3-automatic-cancellation-the-key-advantage-of-task">3. Automatic Cancellation: The Key Advantage of .task</h2>
<p>The most significant benefit of using .task is its automatic cancellation feature. When a view with a .task modifier is dismissed (e.g., the user navigates back), the task is automatically cancelled by the SwiftUI framework.</p>
<p>If you use the Task { ... } workaround inside .onAppear, the task will not be automatically cancelled when the view disappears. This can lead to memory leaks, unnecessary background processing, and potential crashes if the task tries to update the now-deallocated view. To prevent this, you would need to manually store the Task reference and call task.cancel() within the .onDisappear modifier, which adds boilerplate code.</p>
<p>Best Practice Summary:</p>
<table><tbody><tr><td><p>Modifier</p></td><td><p>Primary Use Case</p></td><td><p>Asynchronous Support</p></td><td><p>Automatic Cancellation</p></td></tr><tr><td><p>.onAppear</p></td><td><p>Synchronous operations (e.g., UI animations, logging)</p></td><td><p>No (requires manual Task { ... } wrapper)</p></td><td><p>No (requires manual .onDisappear cleanup)</p></td></tr><tr><td><p>.task</p></td><td><p>Asynchronous operations (e.g., network requests, database access)</p></td><td><p>Yes (natively supports await)</p></td><td><p>Yes (automatically cancels on view dismissal)</p></td></tr></tbody></table>

<p>Conclusion: For any operation involving concurrency (async/await), the .task modifier is the modern, safer, and cleaner choice in SwiftUI. Use .onAppear only for simple, synchronous setup code.</p>
]]></content:encoded></item><item><title><![CDATA[How to create unavailable content in SwiftUI using the ContentUnavailableView.]]></title><description><![CDATA[https://www.youtube.com/watch?v=CJOpSvGa8hU
 

The video is in Portuguese but with English subtitles.

In modern application development, providing a clear and intuitive user experience is paramount. A key aspect of this is gracefully handling moment...]]></description><link>https://blog.faganello.dev.br/how-to-create-unavailable-content-in-swiftui-using-the-contentunavailableview</link><guid isPermaLink="true">https://blog.faganello.dev.br/how-to-create-unavailable-content-in-swiftui-using-the-contentunavailableview</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[SwiftUI]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Sat, 15 Nov 2025 19:16:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763234030865/97756eaf-3502-4807-bade-49e44679c1a4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=CJOpSvGa8hU">https://www.youtube.com/watch?v=CJOpSvGa8hU</a></div>
<p> </p>
<blockquote>
<p>The video is in Portuguese but with English subtitles.</p>
</blockquote>
<p>In modern application development, providing a clear and intuitive user experience is paramount. A key aspect of this is gracefully handling moments when content is not available. Whether it's due to a failed network request, an empty search result, or a feature that requires user data to be populated, presenting a blank screen can leave users confused. With the release of iOS 17, Apple introduced ContentUnavailableView, a powerful and flexible native SwiftUI component designed to elegantly manage these empty or unavailable states. 1</p>
<p>This article provides a comprehensive guide on how to use ContentUnavailableView to improve your SwiftUI applications, complete with practical code examples.</p>
<h2 id="heading-what-is-contentunavailableview">What is ContentUnavailableView?</h2>
<p>ContentUnavailableView is a specialized view that provides a standardized interface for situations where a view’s primary content cannot be displayed. It typically consists of a label (often with an icon), a descriptive text, and optional action buttons. By using this native component, developers can save time and ensure a consistent user experience across the app and the entire Apple ecosystem. 2</p>
<h2 id="heading-basic-implementation">Basic Implementation</h2>
<p>The simplest way to use ContentUnavailableView is by providing a title and a system image. This is ideal for conveying a simple, clear message to the user.</p>
<p>For example, if you have a list of items that is currently empty, you can display a message as an overlay.</p>
<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">MyItemsView</span>: <span class="hljs-title">View</span> </span>{
    @<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> items: [<span class="hljs-type">String</span>] = []

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        <span class="hljs-type">NavigationStack</span> {
            <span class="hljs-type">List</span>(items, id: \.<span class="hljs-keyword">self</span>) { item <span class="hljs-keyword">in</span>
                <span class="hljs-type">Text</span>(item)
            }
            .navigationTitle(<span class="hljs-string">"My Items"</span>)
            .overlay {
                <span class="hljs-keyword">if</span> items.isEmpty {
                    <span class="hljs-type">ContentUnavailableView</span>(<span class="hljs-string">"No Items"</span>, systemImage: <span class="hljs-string">"tray.fill"</span>)
                }
            }
        }
    }
}
</code></pre>
<p>In this code, if the items array is empty, the List will be overlaid with a centered view showing the "tray.fill" icon and the text "No Items".</p>
<h2 id="heading-adding-descriptions-and-actions">Adding Descriptions and Actions</h2>
<p>To provide more context, you can add a description and even interactive actions, such as a "Refresh" button. This is particularly useful for scenarios like network errors, where you want to give the user a way to retry the action.</p>
<p>ContentUnavailableView offers a more advanced initializer that uses ViewBuilder closures for the label, description, and actions.</p>
<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">ProductsView</span>: <span class="hljs-title">View</span> </span>{
    @<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> products: [<span class="hljs-type">String</span>] = []
    @<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> hasError = <span class="hljs-literal">true</span>

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        <span class="hljs-type">NavigationStack</span> {
            <span class="hljs-keyword">if</span> hasError {
                <span class="hljs-type">ContentUnavailableView</span> {
                    <span class="hljs-type">Label</span>(<span class="hljs-string">"Connection Failed"</span>, systemImage: <span class="hljs-string">"wifi.slash"</span>)
                } description: {
                    <span class="hljs-type">Text</span>(<span class="hljs-string">"Please check your internet connection and try again."</span>)
                        .multilineTextAlignment(.center)
                } actions: {
                    <span class="hljs-type">Button</span>(<span class="hljs-string">"Refresh"</span>) {
                        <span class="hljs-comment">// Add logic to refetch data</span>
                        fetchProducts()
                    }
                    .buttonStyle(.borderedProminent)
                }
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-type">List</span>(products, id: \.<span class="hljs-keyword">self</span>) { product <span class="hljs-keyword">in</span>
                    <span class="hljs-type">Text</span>(product)
                }
                .navigationTitle(<span class="hljs-string">"Products"</span>)
            }
        }
        .onAppear(perform: fetchProducts)
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchProducts</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Simulate a network request</span>
        hasError = <span class="hljs-literal">true</span> <span class="hljs-comment">// or false to show content</span>
    }
}
</code></pre>
<p>This example demonstrates a common use case where an error state prompts the user with a clear explanation and a direct way to resolve the issue.</p>
<h2 id="heading-handling-search-results">Handling Search Results</h2>
<p>Another excellent use case for ContentUnavailableView is handling empty states in a searchable list. SwiftUI provides a convenient, pre-configured version specifically for this scenario: <a target="_blank" href="http://ContentUnavailableView.search">ContentUnavailableView.search</a>.</p>
<p>This static instance automatically displays a relevant message, and if you provide the search text, it will even include the user's query in the description.</p>
<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">ContactsView</span>: <span class="hljs-title">View</span> </span>{
    @<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> allContacts = [<span class="hljs-string">"Alice"</span>, <span class="hljs-string">"Bob"</span>, <span class="hljs-string">"Charlie"</span>, <span class="hljs-string">"David"</span>]
    @<span class="hljs-type">State</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> searchText = <span class="hljs-string">""</span>

    <span class="hljs-keyword">var</span> searchResults: [<span class="hljs-type">String</span>] {
        <span class="hljs-keyword">if</span> searchText.isEmpty {
            <span class="hljs-keyword">return</span> allContacts
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> allContacts.<span class="hljs-built_in">filter</span> { $<span class="hljs-number">0</span>.lowercased().<span class="hljs-built_in">contains</span>(searchText.lowercased()) }
        }
    }

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        <span class="hljs-type">NavigationStack</span> {
            <span class="hljs-type">List</span>(searchResults, id: \.<span class="hljs-keyword">self</span>) { contact <span class="hljs-keyword">in</span>
                <span class="hljs-type">Text</span>(contact)
            }
            .navigationTitle(<span class="hljs-string">"Contacts"</span>)
            .searchable(text: $searchText)
            .overlay {
                <span class="hljs-keyword">if</span> searchResults.isEmpty {
                    <span class="hljs-comment">// Shows a message like "No results for \"query\""</span>
                    <span class="hljs-type">ContentUnavailableView</span>.search(text: searchText)
                }
            }
        }
    }
}
</code></pre>
<p>If the user searches for a term that yields no results, this view provides immediate and helpful feedback without any need for manual text formatting.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>ContentUnavailableView is a valuable addition to the SwiftUI framework that simplifies the process of handling empty and error states. By leveraging its basic and advanced customization options, you can create more robust, user-friendly applications that communicate clearly and effectively in any scenario. Adopting this native component not only saves development time but also ensures your app feels polished and consistent with platform standards.</p>
]]></content:encoded></item><item><title><![CDATA[Nova Função 'Count(where:)' no Swift 6 (High-Order Function)]]></title><description><![CDATA[https://youtu.be/otdj_INMcyc
 
O que são as High Order Function
Uma higher order function é uma função que recebe uma outra como argumento, ou uma função que retorna outra função.
Depois dessa breve introdução do que é uma High Order Function, vamos ...]]></description><link>https://blog.faganello.dev.br/nova-funcao-countwhere-no-swift-6-high-order-function</link><guid isPermaLink="true">https://blog.faganello.dev.br/nova-funcao-countwhere-no-swift-6-high-order-function</guid><category><![CDATA[Swift 6]]></category><category><![CDATA[Swift]]></category><category><![CDATA[desenvolvedor]]></category><category><![CDATA[dicas]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[portuguese]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Tue, 17 Sep 2024 15:00:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726531172407/2fef711f-aabc-4dff-a81e-df98dd35d848.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/otdj_INMcyc">https://youtu.be/otdj_INMcyc</a></div>
<p> </p>
<h2 id="heading-o-que-sao-as-high-order-function">O que são as High Order Function</h2>
<p>Uma <strong>higher order function</strong> é uma função que recebe uma outra como argumento, ou uma função que retorna outra função.</p>
<p>Depois dessa breve introdução do que é uma High Order Function, vamos imaginar que temos um array de <strong>Ints</strong> e que precisamos pegar o numero de elementos dentro dele que são múltiplos de 2.</p>
<h3 id="heading-antes-do-swift-6">Antes do Swift 6</h3>
<p>Poderíamos usar diversas formas de pegar <strong>count</strong> de elementos, usando <strong>for</strong> ou <strong>foreach</strong> mas nesse caso estou trazendo como normalmente eu fazia usando o <strong>filter.</strong> Perceba que filter retorna um array com os elemento que retornam true para condição e depois disso pegamos o nosso count.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> array = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">7</span>,<span class="hljs-number">8</span>,<span class="hljs-number">9</span>,<span class="hljs-number">10</span>]
<span class="hljs-keyword">let</span> <span class="hljs-built_in">count</span> = array.<span class="hljs-built_in">filter</span> {
    $<span class="hljs-number">0</span>.isMultiple(of: <span class="hljs-number">2</span>)
}.<span class="hljs-built_in">count</span>
</code></pre>
<h3 id="heading-depois-do-swift-6">Depois do Swift 6</h3>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> array = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">7</span>,<span class="hljs-number">8</span>,<span class="hljs-number">9</span>,<span class="hljs-number">10</span>]
<span class="hljs-keyword">let</span> <span class="hljs-built_in">count</span> = array.<span class="hljs-built_in">count</span> {
    $<span class="hljs-number">0</span>.isMultiple(of: <span class="hljs-number">2</span>)
}
</code></pre>
<p>Perceba que agora no nosso <strong>count</strong> podemos passar um argumento igual no <strong>filter</strong> só que trazendo direto o <strong>count</strong> do array da condição.</p>
<p><strong>Não é nada que vai mudar a nossa vida como desenvolvedor iOS drasticamente mas acho legal quando novas versões do Swift trazem facilidades pro nosso dia a dia e ajudam a remover a verbosidade do código.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Diferenças entre List, VStack e LazyVStack]]></title><description><![CDATA[A primeiro momento você deve pensar que a principal diferença entre eles é que normalmente a List é pouco maleável enquanto o VStack e LazyVStack você tem mais possibilidade de customização.
O tópico que eu quero trazer aqui é na verdade o tópico sob...]]></description><link>https://blog.faganello.dev.br/diferencas-entre-list-vstack-e-lazyvstack</link><guid isPermaLink="true">https://blog.faganello.dev.br/diferencas-entre-list-vstack-e-lazyvstack</guid><category><![CDATA[Swift]]></category><category><![CDATA[SwiftUI]]></category><category><![CDATA[list]]></category><category><![CDATA[stack]]></category><category><![CDATA[dicas]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Mon, 26 Aug 2024 13:00:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724506639062/0aae31a6-7693-435c-9736-e41cbb7d854f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A primeiro momento você deve pensar que a principal diferença entre eles é que normalmente a <strong>List</strong> é pouco maleável enquanto o <strong>VStack</strong> e <strong>LazyVStack</strong> você tem mais possibilidade de customização.</p>
<p><strong>O tópico que eu quero trazer aqui é na verdade o tópico sobre alocação de memória.</strong></p>
<h2 id="heading-alocacao-de-memoria"><strong>Alocação de memória</strong></h2>
<h3 id="heading-vstack"><strong>VStack</strong></h3>
<p>A <strong>VStack</strong> ela aloca todas os elementos na memória mesmo que não estejam visíveis e também não remove por nenhum motivo. Supondo que você monte uma lista com <strong><em>ForEach</em></strong> e <strong>VStack</strong> de 200 elementos, os 200 elementos estarão alocados na memória mesmo que o usuário não faça nada.</p>
<h3 id="heading-lazyvstack">LazyVStack</h3>
<p>A <strong>LazyVStack</strong> ela aloca na memória somente os elementos que estão sendo visto mas não remove os elementos já alocados na memória. Supondo que você monte uma lista com <strong><em>ForEach</em></strong> e <strong>VStack</strong> de 200 elementos, os 10 primeiros elementos estarão alocados na memória e o restante não, entretanto se o usuário fizer scroll até o elemento 200 a memória vai estar com os 200 elementos alocados.</p>
<h3 id="heading-list">List</h3>
<p>A List ela usa o mesmo mecanismos de reutilização da TableView(saudades UIKit), ela aloca e remove os elementos, reutilizando os componentes. Supondo que você monte uma lista com <strong><em>ForEach</em></strong> e <strong>List</strong> de 200 elementos, os 10 primeiros elementos estarão alocados na memória e o restante não e se o usuário realizar o scroll vai remover da memória os elementos que ficaram não visíveis pro usuário e alocar os novos que ficaram visíveis.</p>
<p>Basicamente se a sua tela exibe 10 elementos a <strong>List</strong> vai deixar os 5 elementos antes e depois alocado na memória para melhorar a performance do scroll e quando damos mais alguns scrolls ela reutiliza a memória alocada para esses novos objetos sempre deixando reservado reservado um espaço de memória de 20 itens mais ou menos.</p>
<h2 id="heading-comparacao-real"><strong>Comparação Real</strong></h2>
<h3 id="heading-vstack-1">VStack</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724508786037/48854407-23e8-4c86-b2b7-32b457cdef4a.png" alt class="image--center mx-auto" /></p>
<p>Como vocês podem perceber no exemplo acima a <strong>VStack</strong> nem tinha terminado de carregar todos os elementos mas o uso da memória já estava em 2.07 GB</p>
<h3 id="heading-lazyvstack-1">LazyVStack</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724509197762/317676bc-f051-463c-bf43-38cf30a41ed3.png" alt class="image--center mx-auto" /></p>
<p>Reparem que a <strong>LazyVStack</strong> alocou os elementos de maneira mais rápida e não consumiu memória desnecessária neste primeiro momento, entretanto basta fazer scroll para perceber que a alocação de memória só aumenta, nem fizemos tanto scroll assim e nosso uso de memória foi pra 248,8 MB, mostrando que realmente os objetos renderizados não removidos.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724509212278/6949b982-fe87-4c44-b023-c9e6bbdc6215.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-list-1">List</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724509409264/e748e600-a9e0-43f9-995e-d558c0f7e730.png" alt class="image--center mx-auto" /></p>
<p>A <strong>List</strong> em um primeiro momento se parece muito com a <strong>LazyStack</strong> somente aloca os elementos visíveis na memória mas basta realizar o scroll para vermos a diferença. Fizemos o scroll até o final da lista e pasmem o uso de memória foi de apenas 129,4 MB enquanto na <strong>LazyStack</strong> um simples scroll ja aumentou consideravelmente o uso de memória</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724509524815/49462385-fd53-4f13-b8dc-4a51687eabfd.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>Não sou o dono da verdade eu acho que cada um sabe o que é melhor para usar em cada situação do seu projeto mas atualmente eu tenho feito da seguinte maneira:</p>
<ul>
<li><p>se são elementos estáticos e que não possuem scroll eu utilizo normalmente a <strong>VStack</strong> sem nenhum problema;</p>
</li>
<li><p>se é alguma lista um pouco mais longa mas eu sei a quantidade de itens ou sei que a API retornará por exemplo um numero de itens relativamente baixo eu utilizo a <strong>LazyVStack;</strong></p>
</li>
<li><p>se for alguma lista com scroll infinito ou a API pode me mandar uma lista longa, sem controle de tamanho eu opto por utilizar a <strong>List</strong> mesmo sabendo que ela não é tão maleável assim.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Novidades do SwiftData na WWDC 2024]]></title><description><![CDATA[Não é segredo para ninguém que eu sou fã do SwiftData pelo simples fato de que sofri muito usando o legado CoreData mas como toda boa tecnologia que é lançada algumas features estavam faltando e após a WWDC de 2024 queria trazer algumas novidades que...]]></description><link>https://blog.faganello.dev.br/novidades-do-swiftdata-na-wwdc-2024</link><guid isPermaLink="true">https://blog.faganello.dev.br/novidades-do-swiftdata-na-wwdc-2024</guid><category><![CDATA[Swift]]></category><category><![CDATA[wwdc]]></category><category><![CDATA[WWDC24]]></category><category><![CDATA[SwiftUI]]></category><category><![CDATA[SwiftData]]></category><category><![CDATA[Apple]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Mon, 17 Jun 2024 23:25:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1718666622427/4475e3c3-55cb-42c4-ba71-00397052ea55.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Não é segredo para ninguém que eu sou fã do SwiftData pelo simples fato de que sofri muito usando o legado CoreData mas como toda boa tecnologia que é lançada algumas features estavam faltando e após a WWDC de 2024 queria trazer algumas novidades que fizeram meus olhos brilharem com o SwiftData</p>
<h2 id="heading-unique">#Unique</h2>
<p>A nova atualização trouxe para uma macro super interessante chamada <code>#Unique</code> que permite a criação de uma constraint (Não sei o melhor jeito de traduzir isso rs) para múltiplos atributos. Antigamente precisávamos fazer algum workaround para termos isso.</p>
<p><strong>Antigamente:</strong></p>
<pre><code class="lang-swift">@<span class="hljs-type">Model</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Song</span> </span>{
    @<span class="hljs-type">Attribute</span>(.unique)
    <span class="hljs-keyword">var</span> nameAndSinger: <span class="hljs-type">String</span>    

    <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> singer: <span class="hljs-type">String</span>

    <span class="hljs-keyword">init</span>(name: <span class="hljs-type">String</span>, singer: <span class="hljs-type">String</span>) {
        <span class="hljs-keyword">self</span>.name = name
        <span class="hljs-keyword">self</span>.singer = singer
        nameAndSinger = name + singer
    }
}
</code></pre>
<p><strong>Usando o #Unique:</strong></p>
<pre><code class="lang-swift">@<span class="hljs-type">Model</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Song</span> </span>{
    #<span class="hljs-type">Unique</span>&lt;<span class="hljs-type">Song</span>&gt;([\.name, \.singer])
    <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">var</span> singer: <span class="hljs-type">String</span>

    <span class="hljs-keyword">init</span>(name: <span class="hljs-type">String</span>, singer: <span class="hljs-type">String</span>) {
        <span class="hljs-keyword">self</span>.name = name
        <span class="hljs-keyword">self</span>.singer = singer
    }
}
</code></pre>
<p>Dessa forma o SwiftData nos permite criar diversos objetos com tanto que a combinação de nome e singer seja única. Podemos criar Song com name A pro Faganello e podemos criar um Song com nome A pro cantor Bruno. Uma adição que eu já foi utilizar nos meus projetos porque eu fazia a gambiarra acima para ter o mesmo resultado.</p>
<h2 id="heading-attributepreservevalueondeletion"><code>@Attribute(.preserveValueOnDeletion)</code></h2>
<p>As vezes precisamos criar a famosa exclusão lógica, quem nunca fez algo parecido com <code>isDeleted: Bool.</code> As vezes você queria deixar parcialmente excluido por x tempos caso o usuário tenha se arrependido de apagar e com isso você de fato não apagava o registro agora é possível fazer isso nativamente com o SwiftData</p>
<pre><code class="lang-swift">@<span class="hljs-type">Model</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Song</span> </span>{
    @<span class="hljs-type">Attribute</span>(.preserveValueOnDeletion)
    <span class="hljs-keyword">var</span> album: <span class="hljs-type">Album</span>
}

<span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> deletion = change <span class="hljs-keyword">as</span>? <span class="hljs-type">History</span>.<span class="hljs-type">DefaultDeleteChange</span>&lt;<span class="hljs-type">Song</span>&gt; {
   data = deletion.tombstone[\.album]
}
</code></pre>
<p>Pra saber mais sobre esse <code>History</code>: <a target="_blank" href="https://developer.apple.com/documentation/swiftdata/history/tombstone?changes=latest_minor">Apple Doc History</a></p>
<h2 id="heading-custom-schemas">Custom Schemas</h2>
<p>Podemos agora criar Custom Schemas para nosso Container do SwiftData, isso pode ser interessante se caso você precise por exemplo salvar algo em um formato de arquivo especifico para realizar uma exportação por exemplo.</p>
<pre><code class="lang-swift"><span class="hljs-comment">// Modo Tradicional usando o proprio Schema da Apple</span>
<span class="hljs-keyword">var</span> container: <span class="hljs-type">ModelContainer</span> = {
        <span class="hljs-keyword">do</span> {
            <span class="hljs-keyword">let</span> configuration = <span class="hljs-type">ModelConfiguration</span>(schema: <span class="hljs-type">Schema</span>([<span class="hljs-type">Trip</span>.<span class="hljs-keyword">self</span>]), url: fileURL)
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">ModelContainer</span>(<span class="hljs-keyword">for</span>: <span class="hljs-type">Trip</span>.<span class="hljs-keyword">self</span>, configurations: configuration)
        }
        <span class="hljs-keyword">catch</span> { ... }
    }()

<span class="hljs-comment">// Seu Scheme personalizado, nesse caso algo voltado para json</span>
<span class="hljs-keyword">var</span> container: <span class="hljs-type">ModelContainer</span> = {
        <span class="hljs-keyword">do</span> {
            <span class="hljs-keyword">let</span> configuration = <span class="hljs-type">JSONStoreConfiguration</span>(schema: <span class="hljs-type">Schema</span>([<span class="hljs-type">Song</span>.<span class="hljs-keyword">self</span>]), url: jsonFileURL)
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">ModelContainer</span>(<span class="hljs-keyword">for</span>: <span class="hljs-type">Song</span>.<span class="hljs-keyword">self</span>, configurations: configuration)
        }
        <span class="hljs-keyword">catch</span> { ... }
</code></pre>
<h2 id="heading-previewable"><code>@Previewable</code></h2>
<p>Uma ultima coisa que gostaria de destacar aqui é que aparentemente ficará mais fácil usar o SwiftData com o preview do SwiftUI sem muitas "gambiarras" para fazerem as coisas acontecerem, podemos inserir Mock ModelContainer utilizando um parâmetro na Preview do SwiftUI e para fazer alguma busca nos modelos podemos usar o <code>@Previewable</code> . Acabei brincando pouco com isso até o momento até porque eu já tenho minhas gambiarras funcionando e acabei não investindo tempo nessa mudança. Segue um exemplo de como utilizar.</p>
<pre><code class="lang-swift">
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SampleData</span>: <span class="hljs-title">PreviewModifier</span> </span>{
    <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeSharedContext</span><span class="hljs-params">()</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">ModelContainer</span> {
        <span class="hljs-keyword">let</span> config = <span class="hljs-type">ModelConfiguration</span>(isStoredInMemoryOnly: <span class="hljs-literal">true</span>)
        <span class="hljs-keyword">let</span> container = <span class="hljs-keyword">try</span> <span class="hljs-type">ModelContainer</span>(<span class="hljs-keyword">for</span>: <span class="hljs-type">Trip</span>.<span class="hljs-keyword">self</span>, configurations: config)
        <span class="hljs-type">Trip</span>.makeSampleTrips(<span class="hljs-keyword">in</span>: container)
        <span class="hljs-keyword">return</span> container
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">body</span><span class="hljs-params">(content: Content, context: ModelContainer)</span></span> -&gt; some <span class="hljs-type">View</span> {
        content.modelContainer(context)
    }
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">PreviewTrait</span> <span class="hljs-title">where</span> <span class="hljs-title">T</span> == <span class="hljs-title">Preview</span>.<span class="hljs-title">ViewTraits</span> </span>{
    @<span class="hljs-type">MainActor</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> sampleData: <span class="hljs-type">Self</span> = .modifier(<span class="hljs-type">SampleData</span>())
}

<span class="hljs-keyword">import</span> SwiftUI
<span class="hljs-keyword">import</span> SwiftData

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ContentView</span>: <span class="hljs-title">View</span> </span>{
    @<span class="hljs-type">Query</span>
    <span class="hljs-keyword">var</span> trips: [<span class="hljs-type">Trip</span>]

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        ...
    }
}

#<span class="hljs-type">Preview</span>(traits: .sampleData) {
    <span class="hljs-type">ContentView</span>()
}

<span class="hljs-keyword">import</span> SwiftUI
<span class="hljs-keyword">import</span> SwiftData

#<span class="hljs-type">Preview</span>(traits: .sampleData) {
    @<span class="hljs-type">Previewable</span> @<span class="hljs-type">Query</span> <span class="hljs-keyword">var</span> trips: [<span class="hljs-type">Trip</span>]
    <span class="hljs-type">BucketListItemView</span>(trip: trips.first)
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[async let - funções assíncronas (async) em Paralelo]]></title><description><![CDATA[Com a introdução da nova maneira de realizar chamadas assíncronas no Swift podem surgir algumas duvidas em como executar chamadas em paralelo. O modo "antigo" de ser feito usando os famosos completionHandlers precisávamos utilizar uma classe chamada ...]]></description><link>https://blog.faganello.dev.br/async-let-funcoes-assincronas-async-em-paralelo</link><guid isPermaLink="true">https://blog.faganello.dev.br/async-let-funcoes-assincronas-async-em-paralelo</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[SwiftUI]]></category><category><![CDATA[http requests]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Wed, 29 May 2024 15:00:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716938536381/4e23db30-e6ac-43f0-b3f9-93e0b1e67d19.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Com a introdução da nova maneira de realizar chamadas assíncronas no Swift podem surgir algumas duvidas em como executar chamadas em paralelo. O modo "antigo" de ser feito usando os famosos completionHandlers precisávamos utilizar uma classe chamada <code>DispatchGroup()</code> para conseguirmos realizar esse feito.</p>
<h3 id="heading-como-usar-async-let">Como usar async let</h3>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loadWeather</span><span class="hljs-params">(lat: Double, long: Double, index: Int)</span></span> async -&gt; <span class="hljs-type">Weather</span> {
    <span class="hljs-keyword">let</span> url = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://api.openweathermap.org/data/2.5/weather?lat=\(lat)&amp;lon=\(long)&amp;appid=*****"</span>)!
    <span class="hljs-keyword">let</span> request = <span class="hljs-type">URLRequest</span>(url: url)
    <span class="hljs-keyword">let</span> (data, <span class="hljs-number">_</span>) = <span class="hljs-keyword">try</span>! await <span class="hljs-type">URLSession</span>.shared.data(<span class="hljs-keyword">for</span>: request, delegate: <span class="hljs-literal">nil</span>)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Done \(index)"</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span>! <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">Weather</span>.<span class="hljs-keyword">self</span>, from: data)
}
</code></pre>
<p>Temos o nosso código para fazer uma request para nossa API de tempo e retornar nosso modelo. Adicionei um print index para ficar mais fácil a visualização mas isso não é necessário</p>
<p>Do jeito tradicional criaríamos uma função nos moldes a seguir</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getWeather</span><span class="hljs-params">()</span></span> {
    <span class="hljs-type">Task</span> {
        <span class="hljs-keyword">let</span> saoPaulo = await loadWeather(lat: -<span class="hljs-number">23.5489</span>, long: -<span class="hljs-number">46.6388</span>, index: <span class="hljs-number">1</span>)
        <span class="hljs-keyword">let</span> rio = await loadWeather(lat:  -<span class="hljs-number">22.9035</span>, long: -<span class="hljs-number">43.2096</span>, index: <span class="hljs-number">2</span>)
        <span class="hljs-keyword">let</span> bahia = await loadWeather(lat: -<span class="hljs-number">11.4098</span>, long:  -<span class="hljs-number">41.2808</span>, index: <span class="hljs-number">3</span>)
        <span class="hljs-keyword">let</span> tempo = [saoPaulo, rio, bahia]
    }
}
</code></pre>
<p>Se executamos esse código vamos perceber que o nosso log vai estar assim</p>
<pre><code class="lang-abap">Done <span class="hljs-number">1</span>
Done <span class="hljs-number">2</span>
Done <span class="hljs-number">3</span>
</code></pre>
<p>Se você reparar bem não existe nenhum motivo para executar essas requests em serie, uma não depende da outra e como faríamos isso com async await? Usando o novo termo <code>async let</code> o código ficará assim:</p>
<pre><code class="lang-swift"><span class="hljs-type">Task</span> {
    async <span class="hljs-keyword">let</span> saoPaulo = loadWeather(lat: -<span class="hljs-number">23.5489</span>, long: -<span class="hljs-number">46.6388</span>, index: <span class="hljs-number">1</span>)
    async <span class="hljs-keyword">let</span> rio = loadWeather(lat:  -<span class="hljs-number">22.9035</span>, long: -<span class="hljs-number">43.2096</span>, index: <span class="hljs-number">2</span>)
    async <span class="hljs-keyword">let</span> bahia = loadWeather(lat: -<span class="hljs-number">11.4098</span>, long:  -<span class="hljs-number">41.2808</span>, index: <span class="hljs-number">3</span>)
    <span class="hljs-keyword">let</span> tempo = await [saoPaulo, rio, bahia]
}
</code></pre>
<p>Percebam que agora só temos um <code>await</code> e perceba que ele fica posicionado no momento em que tentamos acessar a variável em questão, nesse caso antes de criamos o nosso array.</p>
<p>Se executamos o código agora podemos receber diferentes tipos de log, no meu caso foi:</p>
<pre><code class="lang-abap">Done <span class="hljs-number">1</span>
Done <span class="hljs-number">3</span>
Done <span class="hljs-number">2</span>
</code></pre>
<p><em>Lembrando que só o async let só pode ser usado dentro de funções.</em></p>
]]></content:encoded></item><item><title><![CDATA[Migrando seu Completion Handler para Async await]]></title><description><![CDATA[Códigos legados em swift na maioria das vezes utilizam o Completion Handler para realizar alguma tarefa assíncrona e notificar quem está chamando de que a operação foi realizada e que o e dados está pronto para ser retornado. Observando o código abai...]]></description><link>https://blog.faganello.dev.br/migrando-seu-completion-handler-para-async-await</link><guid isPermaLink="true">https://blog.faganello.dev.br/migrando-seu-completion-handler-para-async-await</guid><category><![CDATA[CompletionHandler]]></category><category><![CDATA[Swift]]></category><category><![CDATA[SwiftUI]]></category><category><![CDATA[iOS]]></category><category><![CDATA[ios app development]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Sat, 11 May 2024 15:19:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715438591539/412d45dc-7ce0-40fc-ac39-db738404037d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Códigos legados em swift na maioria das vezes utilizam o Completion Handler para realizar alguma tarefa assíncrona e notificar quem está chamando de que a operação foi realizada e que o e dados está pronto para ser retornado. Observando o código abaixo:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Joke</span>: <span class="hljs-title">Decodable</span> </span>{
    <span class="hljs-keyword">let</span> icon_url: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> value: <span class="hljs-type">String</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchRandomJoke</span><span class="hljs-params">(completion: @escaping <span class="hljs-params">(Joke?)</span></span></span> -&gt; <span class="hljs-type">Void</span>) {
    <span class="hljs-keyword">let</span> url = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://api.chucknorris.io/jokes/random"</span>)!

    <span class="hljs-type">URLSession</span>.shared.dataTask(with: url) { data, response, error <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> data = data {
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> messages = <span class="hljs-keyword">try</span>? <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">Joke</span>.<span class="hljs-keyword">self</span>, from: data) {
                completion(messages)
                <span class="hljs-keyword">return</span>
            }
        }

        completion(<span class="hljs-literal">nil</span>)

    }.resume()
}
</code></pre>
<p>O código acima está escrito da maneira "antiga" de como fazíamos requests em Swift.</p>
<p>Vamos supor que você está desenvolvendo uma nova feature em SwiftUI que vai utilizar a mesma request <code>fecthRandomJoke</code> que outras feature legadas utilizam, você pode acabar com alguns problemas:</p>
<ul>
<li><p>Async/Await tem alguns comportamentos estranhos rodando com CompletionHandler</p>
</li>
<li><p>Pode pensar em criar 2 requests uma nova e outra legada o problema seria se o modelo mudasse com alguma frequência, você teria que alterar em 2 lugares</p>
</li>
<li><p>Você pode editar o código legado para usar o Async/Await mas provavelmente teria que mudar em vários lugares porque o código legado estará todo em CompletionHandler</p>
</li>
</ul>
<h3 id="heading-existe-salvacao">Existe salvação?</h3>
<p>Claro! Se você pensou em criar 2 requests você quase acertou o Swift nos permite utilizar uma função chamada <code>withCheckedContinuation</code> para fazermos essa alteração. Veja o código a seguir:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Joke</span>: <span class="hljs-title">Decodable</span> </span>{
    <span class="hljs-keyword">let</span> icon_url: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> value: <span class="hljs-type">String</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchRandomJoke</span><span class="hljs-params">(completion: @escaping <span class="hljs-params">(Joke?)</span></span></span> -&gt; <span class="hljs-type">Void</span>) {
    <span class="hljs-keyword">let</span> url = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://api.chucknorris.io/jokes/random"</span>)!

    <span class="hljs-type">URLSession</span>.shared.dataTask(with: url) { data, response, error <span class="hljs-keyword">in</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> data = data {
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> messages = <span class="hljs-keyword">try</span>? <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">Joke</span>.<span class="hljs-keyword">self</span>, from: data) {
                completion(messages)
                <span class="hljs-keyword">return</span>
            }
        }

        completion(<span class="hljs-literal">nil</span>)

    }.resume()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchRandomJoke</span><span class="hljs-params">()</span></span> async -&gt; <span class="hljs-type">Message?</span> {
    await withCheckedContinuation { continuation <span class="hljs-keyword">in</span>
        fetchRandomJoke { joke <span class="hljs-keyword">in</span>
            continuation.resume(returning: joke)
        }
    }
}

<span class="hljs-keyword">let</span> joke = await fetchRandomJoke()
<span class="hljs-built_in">print</span>(joke.value)
</code></pre>
<p>Dessa maneira mantemos somente uma request então o problema de alterar em dois lugares foi solucionado e agora você pode utilizar o novo jeito do Swift para lidar com chamadas assíncronas.</p>
]]></content:encoded></item><item><title><![CDATA[Guia sobre SwiftData - Models]]></title><description><![CDATA[O que é o SwiftData
Primeiro de tudo é que não podemos confundir o SwiftData com um Banco de Dados, o SwiftData é basicamente um ORM (Mapeamento objeto-relacional (Object–relational mapping)). Para curiosidade dos demais, o banco de dados padrão do i...]]></description><link>https://blog.faganello.dev.br/guia-sobre-swiftdata-models</link><guid isPermaLink="true">https://blog.faganello.dev.br/guia-sobre-swiftdata-models</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[SwiftData]]></category><category><![CDATA[Core data]]></category><dc:creator><![CDATA[Dev Faganello]]></dc:creator><pubDate>Wed, 08 Nov 2023 14:07:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/oyXis2kALVg/upload/9a3d8e19bfc84bed526b0a2b8a2fd925.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-o-que-e-o-swiftdata">O que é o SwiftData</h2>
<p>Primeiro de tudo é que não podemos confundir o SwiftData com um Banco de Dados, o SwiftData é basicamente um ORM (Mapeamento objeto-relacional (Object–relational mapping)). Para curiosidade dos demais, o banco de dados padrão do iOS é o SQLite.</p>
<p>SwiftData é uma framework lançada para substituir a antiga framework chamada CoreData que vem da era do Objective-C (Saudades colchetes). Mesmo seu projeto sendo em Swift/SwftUI você conseguia integrar o CoreData, mas não é uma solução nativa para essas ferramentas.</p>
<p>SwiftData foi introduzido no iOS 17 e não funciona nas versões abaixo, uma pena para projeto que ainda não podem migrar a versão minima.</p>
<h2 id="heading-models">Models</h2>
<p>Vamos falar sobre as Models, que basicamente seriam nossas tabelas em um banco de dados relacional.</p>
<pre><code class="lang-swift">@<span class="hljs-type">Model</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span> </span>{
  <span class="hljs-keyword">var</span> title: <span class="hljs-type">String</span>
  <span class="hljs-keyword">var</span> pages: <span class="hljs-type">Double</span>
  <span class="hljs-keyword">var</span> theme: <span class="hljs-type">String</span>
}
</code></pre>
<p>Primeiro de tudo que precisamos adicionar a Macro <strong>@Model</strong> e não podemos trabalhar com <strong>Struct</strong>, temos que obrigatóriamente com as <strong>class</strong>. Basicamente esse é um modelo da classe Livro que será salvo no nosso banco de dados.</p>
<h3 id="heading-atributos">Atributos</h3>
<p>Você deve estar se perguntando, mas se eu quiser adicionar um atributo unico, pode ser o <strong>title</strong> ou talvez um <strong>id.</strong> Nesse caso vamos adicionar outra macro na frente do nosso atríbuto.</p>
<pre><code class="lang-swift">@<span class="hljs-type">Model</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span> </span>{
  @<span class="hljs-type">Attribute</span>(.unique) <span class="hljs-keyword">var</span> title: <span class="hljs-type">String</span>
  <span class="hljs-keyword">var</span> pages: <span class="hljs-type">Double</span>
  <span class="hljs-keyword">var</span> theme: <span class="hljs-type">String</span>
}
</code></pre>
<p>Dessa forma não conseguiremos salvar dois livros com o mesmo título.</p>
<p>Vou deixar uma lista aqui de atríbutos que podemos ser usados:</p>
<pre><code class="lang-swift"> <span class="hljs-comment">/// Ensures the property's value is unique across all models of the same type.</span>
 <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> unique: <span class="hljs-type">Schema</span>.<span class="hljs-type">Attribute</span>.<span class="hljs-type">Option</span> { <span class="hljs-keyword">get</span> }

<span class="hljs-comment">/// Transforms the property's value between an in-memory form and a persisted form.</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">transformable</span><span class="hljs-params">(by transformerType: ValueTransformer.<span class="hljs-keyword">Type</span>)</span></span> -&gt; <span class="hljs-type">Schema</span>.<span class="hljs-type">Attribute</span>.<span class="hljs-type">Option</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">transformable</span><span class="hljs-params">(by transformerName: String)</span></span> -&gt; <span class="hljs-type">Schema</span>.<span class="hljs-type">Attribute</span>.<span class="hljs-type">Option</span>

<span class="hljs-comment">/// Stores the property's value as binary data adjacent to the model storage.</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> externalStorage: <span class="hljs-type">Schema</span>.<span class="hljs-type">Attribute</span>.<span class="hljs-type">Option</span> { <span class="hljs-keyword">get</span> }

<span class="hljs-comment">/// Stores the property's value in an encrypted form.</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> allowsCloudEncryption: <span class="hljs-type">Schema</span>.<span class="hljs-type">Attribute</span>.<span class="hljs-type">Option</span> { <span class="hljs-keyword">get</span> }

<span class="hljs-comment">/// Preserves the property's value in the persistent history when the context deletes the owning model.</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> preserveValueOnDeletion: <span class="hljs-type">Schema</span>.<span class="hljs-type">Attribute</span>.<span class="hljs-type">Option</span> { <span class="hljs-keyword">get</span> }

<span class="hljs-comment">/// Track changes to this property but do not persist</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> ephemeral: <span class="hljs-type">Schema</span>.<span class="hljs-type">Attribute</span>.<span class="hljs-type">Option</span> { <span class="hljs-keyword">get</span> }

<span class="hljs-comment">/// Indexes the property's value so it can appear in Spotlight search results.</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> spotlight: <span class="hljs-type">Schema</span>.<span class="hljs-type">Attribute</span>.<span class="hljs-type">Option</span> { <span class="hljs-keyword">get</span> }
</code></pre>
<h3 id="heading-atributos-transitorios-transient-attributes">Atributos Transitórios (<strong>transient attributes)</strong></h3>
<p>São atributos que fazem parte da nossa model, mas que não queremos salvar no banco de dados por algum motivo específico.</p>
<pre><code class="lang-swift">@<span class="hljs-type">Model</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span> </span>{
  @<span class="hljs-type">Attribute</span>(.unique) <span class="hljs-keyword">var</span> title: <span class="hljs-type">String</span>
  <span class="hljs-keyword">var</span> pages: <span class="hljs-type">Double</span>
  <span class="hljs-keyword">var</span> theme: <span class="hljs-type">String</span>

  <span class="hljs-comment">// Will not save this two properties </span>
  @<span class="hljs-type">Transient</span> <span class="hljs-keyword">var</span> readingSpeedPerPage = <span class="hljs-number">0</span>
  <span class="hljs-keyword">var</span> fullTitle: <span class="hljs-type">String</span> {
        <span class="hljs-string">"\(title) - \(theme)"</span>
  }
}
</code></pre>
<p>SwiftData vai automaticamente salvar somente as <strong>Stored Properties</strong> do seu modelo, qualquer <strong>Computed Properties</strong> será tratada como transient, caso você queria que alguma Stored Property seja temporário basta colocar a macro <strong>@Transient</strong> na frente.</p>
<h2 id="heading-relationship">Relationship</h2>
<pre><code class="lang-swift">@<span class="hljs-type">Model</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span> </span>{
  @<span class="hljs-type">Attribute</span>(.unique) <span class="hljs-keyword">var</span> title: <span class="hljs-type">String</span>
  <span class="hljs-keyword">var</span> pages: <span class="hljs-type">Double</span>
  <span class="hljs-keyword">var</span> theme: <span class="hljs-type">String</span>

  <span class="hljs-comment">// Will not save this two properties </span>
  @<span class="hljs-type">Transient</span> <span class="hljs-keyword">var</span> readingSpeedPerPage = <span class="hljs-number">0</span>
  <span class="hljs-keyword">var</span> fullTitle: <span class="hljs-type">String</span> {
        <span class="hljs-string">"\(title) - \(theme)"</span>
  }
}

@<span class="hljs-type">Model</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Author</span> </span>{
  @<span class="hljs-type">Attribute</span>(.unique) <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
  @<span class="hljs-type">Relationship</span>(deleteRule: .cascade) <span class="hljs-keyword">var</span> books: [<span class="hljs-type">Books</span>]
}
</code></pre>
<p>Para criar uma relação entre duas classes precisamos criar um atributo, nesse caso eu estou dizendo que o Author conhece seus livros mas o livro não sabe quem é seu Author, e adicionar a Macro <strong>@Relationship</strong> e adicionar o tipo de regra de exclusão. Segue uma lista das regras disponíveis:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">case</span> cascade
<span class="hljs-type">A</span> rule that deletes any related models.

<span class="hljs-keyword">case</span> deny
<span class="hljs-type">A</span> rule that prevents the deletion of a model because it <span class="hljs-built_in">contains</span> one or more references to other models.

<span class="hljs-keyword">case</span> noAction
<span class="hljs-type">A</span> rule that doesn’t make changes to any related models.

<span class="hljs-keyword">case</span> nullify
<span class="hljs-type">A</span> rule that nullifies the related model’s reference to the deleted model.
</code></pre>
<p>Basicamente:</p>
<ul>
<li><p>Cascade - Se apagarmos o Author todos os livros que ele tem como referencia vão ser apagados.</p>
</li>
<li><p>Deny - Só vai deixar apagar o Author depois que apagarmos todos os livros</p>
</li>
<li><p>noAction - Apaga o Author mas deixa os livros salvos</p>
</li>
<li><p>nullify - Se nossa classe Book conhecesse a classe Author essa propriedade ficaria com valor nil se apagarmos o Author desses livros.</p>
</li>
</ul>
<p><strong>Por padrão o SwiftData a regra de deleção é nullify</strong></p>
<p>Para terminar esse artigo vamos fazer nossa classe livro conhecer o autor, porque existem dois jeito de fazer isso.</p>
<p>Uma maneira é deixar explicito na macro de relação</p>
<pre><code class="lang-swift">@<span class="hljs-type">Model</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span> </span>{
  @<span class="hljs-type">Attribute</span>(.unique) <span class="hljs-keyword">var</span> title: <span class="hljs-type">String</span>
  <span class="hljs-keyword">var</span> pages: <span class="hljs-type">Double</span>
  <span class="hljs-keyword">var</span> theme: <span class="hljs-type">String</span>
  <span class="hljs-keyword">var</span> author: <span class="hljs-type">Author</span>

  <span class="hljs-comment">// Will not save this two properties </span>
  @<span class="hljs-type">Transient</span> <span class="hljs-keyword">var</span> readingSpeedPerPage = <span class="hljs-number">0</span>
  <span class="hljs-keyword">var</span> fullTitle: <span class="hljs-type">String</span> {
        <span class="hljs-string">"\(title) - \(theme)"</span>
  }
}

@<span class="hljs-type">Model</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Author</span> </span>{
  @<span class="hljs-type">Attribute</span>(.unique) <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
  @<span class="hljs-type">Relationship</span>(deleteRule: .cascade,  inverse: \<span class="hljs-type">Book</span>.author) <span class="hljs-keyword">var</span> books: [<span class="hljs-type">Books</span>]
}
</code></pre>
<p>A outra é deixar o atributo Author como <strong>Optional</strong></p>
<pre><code class="lang-swift">@<span class="hljs-type">Model</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span> </span>{
  @<span class="hljs-type">Attribute</span>(.unique) <span class="hljs-keyword">var</span> title: <span class="hljs-type">String</span>
  <span class="hljs-keyword">var</span> pages: <span class="hljs-type">Double</span>
  <span class="hljs-keyword">var</span> theme: <span class="hljs-type">String</span>
  <span class="hljs-keyword">var</span> author: <span class="hljs-type">Author?</span>

  <span class="hljs-comment">// Will not save this two properties </span>
  @<span class="hljs-type">Transient</span> <span class="hljs-keyword">var</span> readingSpeedPerPage = <span class="hljs-number">0</span>
  <span class="hljs-keyword">var</span> fullTitle: <span class="hljs-type">String</span> {
        <span class="hljs-string">"\(title) - \(theme)"</span>
  }
}

@<span class="hljs-type">Model</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Author</span> </span>{
  @<span class="hljs-type">Attribute</span>(.unique) <span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span>
  @<span class="hljs-type">Relationship</span>(deleteRule: .cascade) <span class="hljs-keyword">var</span> books: [<span class="hljs-type">Books</span>]
}
</code></pre>
]]></content:encoded></item></channel></rss>