{"id":"l-sw-4-1","courseId":"swift-essentials","lang":"en","kind":"lesson","summary":"Write async functions, start work from SwiftUI with .task, run calls in parallel with async let and task groups, and read Swift 6 data-race errors about actors and Sendable.","html":"<p>Pocket Budget is about to call a server for exchange rates. A request might take 200 milliseconds or eight seconds on a train. If the main thread, which draws the UI and handles touches, sat waiting for it, the app would freeze and iOS might eventually kill it. Swift’s concurrency model lets you write code that waits without blocking, in a style that reads top to bottom, and Swift 6 adds a compiler that proves your concurrent code has no data races.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">async and await</h2>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">RateService.swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Foundation</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">struct</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> RateService</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> fetchRate</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> base: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">to</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> target: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> throws</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Decimal {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        try</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Task.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">sleep</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: .</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">milliseconds</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">300</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// stands in for a network call</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> target </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">==</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"EUR\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ?</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0.92</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> :</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0.79</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p><code dir=\"ltr\">async</code> in the signature means “this function can pause”. Every call to it is marked <code dir=\"ltr\">await</code>, which marks a <strong>suspension point</strong>: the function may pause there, the thread goes off to do other work (like scrolling a list), and the function resumes when the result is ready. <code dir=\"ltr\">async</code> combines with <code dir=\"ltr\">throws</code>, so the call is <code dir=\"ltr\">try await</code>. In the next lesson the <code dir=\"ltr\">Task.sleep</code> becomes a real request.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Starting async work from SwiftUI</h2>\n<p>Async functions can only be called from an async context. SwiftUI gives you one with the <code dir=\"ltr\">.task</code> modifier:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@MainActor</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">@Observable</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">final</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> class</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> BudgetStore</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> expenses: [Expense] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> []</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> euroRate: Decimal</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> isLoadingRate </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> false</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    private</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> rates </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> RateService</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> loadEuroRate</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        isLoadingRate </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> true</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        defer</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { isLoadingRate </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> false</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        do</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            euroRate </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> try</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> rates.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">fetchRate</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"USD\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">to</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"EUR\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            euroRate </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> nil</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">struct</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> RateBanner</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">View </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> store: BudgetStore</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> body: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">some</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> View {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">        Group</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">            if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> store.isLoadingRate {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">                ProgressView</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">else</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> if</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> rate </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> store.euroRate {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">                Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"1 USD = </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">rate.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">formatted</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">()</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> EUR\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">else</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">                Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Rate unavailable\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        .</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">task</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">            await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> store.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">loadEuroRate</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p><code dir=\"ltr\">.task</code> starts when the view appears and is <strong>cancelled</strong> automatically when it disappears, so a user who leaves the screen doesn’t leave a request running in the background. For a button tap, wrap the call in <code dir=\"ltr\">Task { await store.loadEuroRate() }</code>, which starts new top-level work. <code dir=\"ltr\">defer</code> resets the loading flag however the function exits.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Structured concurrency: parallel children</h2>\n<p>Two independent requests shouldn’t wait for each other. <code dir=\"ltr\">async let</code> starts work immediately and lets you collect it later:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> loadBothRates</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">using</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> service: RateService) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> throws</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (eur: Decimal, gbp: Decimal) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> eur </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> service.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">fetchRate</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"USD\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">to</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"EUR\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> gbp </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> service.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">fetchRate</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"USD\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">to</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"GBP\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> try</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (eur, gbp)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>Both requests run at the same time, so this takes about 300 ms instead of 600. For a number of tasks you only know at runtime, use a task group:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> loadRates</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> codes: [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">using</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> service: RateService) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> throws</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: Decimal] {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    try</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> withThrowingTaskGroup</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">of</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: (</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, Decimal).</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">self</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) { group </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> code </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> codes {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            group.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">addTask</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">                (code, </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">try</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> service.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">fetchRate</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">from</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"USD\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">to</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: code))</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> rates: [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: Decimal] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        for</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> try</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (code, rate) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> group {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            rates[code] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> rate</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> rates</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>This is <strong>structured</strong> concurrency: child tasks live inside their parent’s scope. The function can’t return while a child is still running, if one child throws the others are cancelled, and cancelling the parent cancels every child. Notice that each child <em>returns</em> its result and the parent assembles the dictionary. Children never write to shared state.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">Data races and the Swift 6 compiler</h2>\n<p>A <strong>data race</strong> happens when two threads access the same memory at the same time and at least one writes. The result is corrupted data or a crash that appears once in a thousand runs. Swift 6 language mode turns the checks for this into compile errors, organised around <strong>isolation</strong>: every piece of mutable state belongs to exactly one domain, and only code in that domain may touch it directly.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 240\" role=\"img\" aria-labelledby=\"l-sw-4-1-t1\"><defs><marker id=\"arrow-l-sw-4-1-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-sw-4-1-t1\">Three isolation domains. The main actor holds the views and BudgetStore. The RateCache actor holds the cached rates. Child tasks fetch rates concurrently. Only Sendable values, such as a Decimal rate, cross between domains, and every crossing is an await.</title>\n  <rect class=\"d-box-primary\" x=\"15\" y=\"30\" width=\"220\" height=\"150\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"125\" y=\"58\" text-anchor=\"middle\">@MainActor</text>\n  <text class=\"d-code\" x=\"125\" y=\"95\" text-anchor=\"middle\">Views</text>\n  <text class=\"d-code\" x=\"125\" y=\"125\" text-anchor=\"middle\">BudgetStore</text>\n  <rect class=\"d-box-accent\" x=\"465\" y=\"30\" width=\"220\" height=\"150\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"575\" y=\"58\" text-anchor=\"middle\">actor RateCache</text>\n  <text class=\"d-code\" x=\"575\" y=\"105\" text-anchor=\"middle\">rates: [String: Decimal]</text>\n  <rect class=\"d-box-success\" x=\"265\" y=\"70\" width=\"170\" height=\"70\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"350\" y=\"100\" text-anchor=\"middle\">child tasks</text>\n  <text class=\"d-label-muted\" x=\"350\" y=\"122\" text-anchor=\"middle\">fetch in parallel</text>\n  <path class=\"d-arrow\" d=\"M265 90 L237 90\" marker-end=\"url(#arrow-l-sw-4-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M435 90 L463 90\" marker-end=\"url(#arrow-l-sw-4-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"350\" y=\"215\" text-anchor=\"middle\">Only Sendable values cross the boundaries, and each crossing is an await</text>\n</svg><figcaption>Isolation domains in Pocket Budget</figcaption></figure>\n<p>Three tools cover almost every case in an app:</p>\n<p><strong>The main actor.</strong> <code dir=\"ltr\">@MainActor</code> isolates a type to the main thread. UI state belongs there, which is why <code dir=\"ltr\">BudgetStore</code> is marked <code dir=\"ltr\">@MainActor</code> above: the views that read it run on the main actor, and now the compiler guarantees nothing else writes to it. New app projects in Xcode 26 and later set the <strong>Default Actor Isolation</strong> build setting to <code dir=\"ltr\">MainActor</code>, so your app code is main-actor isolated unless you say otherwise. It’s a sensible default: most app code is UI code.</p>\n<p><strong>Actors.</strong> An <code dir=\"ltr\">actor</code> is a reference type that protects its own state by running one call at a time. Code outside the actor must <code dir=\"ltr\">await</code> to reach it:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">actor</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> RateCache</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    private</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> rates: [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: Decimal] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> rate</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> code: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Decimal</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        rates[code]</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> save</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">_</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> rate: Decimal, </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> code: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        rates[code] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> rate</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// From anywhere: await cache.save(0.92, for: \"EUR\")</span></span></code></pre></div>\n<p><strong>Sendable values.</strong> A type is <code dir=\"ltr\">Sendable</code> when it’s safe to pass between domains. Value types made of Sendable parts (<code dir=\"ltr\">String</code>, <code dir=\"ltr\">Decimal</code>, your <code dir=\"ltr\">Expense</code> struct) qualify automatically, as do actors and main-actor-isolated classes. A plain class with <code dir=\"ltr\">var</code> properties doesn’t, and the compiler stops you from sharing one between tasks.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Sharing a mutable object between child tasks</span></p>\n<p>Writing <code dir=\"ltr\">group.addTask { tally.total += amount }</code>, where <code dir=\"ltr\">tally</code> is an ordinary class, fails to build in Swift 6: the compiler reports that passing the closure “risks causing data races”, or, under main-actor default isolation, that a main actor-isolated property can’t be mutated from a nonisolated context. Don’t silence it. Have each child return its value and combine the results in the parent, or move the state into an actor.</p>\n</aside>\n<aside class=\"callout callout-note\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/></svg><span><span class=\"callout-kind\">Note</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Making it approachable</span></p>\n<p>Swift 6.2 introduced a set of options Xcode calls <strong>Approachable Concurrency</strong>. With them, a <code dir=\"ltr\">nonisolated</code> async function runs on the actor of whoever called it unless you mark it <code dir=\"ltr\">@concurrent</code>, which means most app code stays on the main actor and only work you choose moves off it. You’ll see these settings in a new project’s build settings; leave them on.</p>\n</aside>\n<p>That’s the vocabulary: <code dir=\"ltr\">async</code>, <code dir=\"ltr\">await</code>, tasks, actors, <code dir=\"ltr\">Sendable</code>. You don’t need to master every rule at once; read each compiler error as a question about which domain owns the data. Next you replace the pretend <code dir=\"ltr\">Task.sleep</code> with a real request and decode the JSON it returns.</p>\n","toc":[{"id":"sec-1","text":"async and await","level":2},{"id":"sec-2","text":"Starting async work from SwiftUI","level":2},{"id":"sec-3","text":"Structured concurrency: parallel children","level":2},{"id":"sec-4","text":"Data races and the Swift 6 compiler","level":2}],"takeaways":["<code dir=\"ltr\">await</code> marks a point where a function may pause; the thread is free to do other work, such as keeping the UI responsive, while it waits.","Start async work from a view with <code dir=\"ltr\">.task</code>, which SwiftUI cancels automatically when the view disappears.","<code dir=\"ltr\">async let</code> and task groups run work in parallel as child tasks that must finish, or be cancelled, before their parent returns.","Swift 6 refuses to compile code where two tasks could touch the same mutable state at once; actors and <code dir=\"ltr\">Sendable</code> types are how you satisfy it.","Keep UI state on the main actor with <code dir=\"ltr\">@MainActor</code>; new Xcode projects make main-actor isolation the default for app code."],"further":[{"title":"Concurrency (The Swift Programming Language)","url":"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/"},{"title":"Swift 6 Migration Guide","url":"https://www.swift.org/migration/documentation/migrationguide/"},{"title":"Updating an app to use strict concurrency","url":"https://developer.apple.com/documentation/swift/updating-an-app-to-use-strict-concurrency"}],"quiz":[{"q":"<p>A view loads exchange rates in <code dir=\"ltr\">.task { await store.loadEuroRate() }</code>. The user leaves the screen before the request finishes. What happens?</p>","options":[{"text":"The task keeps running and updates the store later.","why":"That’s what an unstructured <code dir=\"ltr\">Task { }</code> inside a button would do. <code dir=\"ltr\">.task</code> is tied to the view’s lifetime."},{"text":"SwiftUI cancels the task, and cancellation-aware calls like <code dir=\"ltr\">Task.sleep</code> or URLSession requests throw.","why":"Correct. Cancellation is cooperative; well-behaved async APIs notice it and stop early."},{"text":"The app crashes because the view no longer exists.","why":"The task holds what it needs; it doesn’t depend on the view struct staying around."}],"answer":1},{"q":"<p><code dir=\"ltr\">async let eur = fetch(&quot;EUR&quot;)</code> and <code dir=\"ltr\">async let gbp = fetch(&quot;GBP&quot;)</code>, each taking about 300 ms, followed by <code dir=\"ltr\">try await (eur, gbp)</code>. Roughly how long does it take?</p>","options":[{"text":"About 300 ms, because both requests run at the same time.","why":"Correct. Each <code dir=\"ltr\">async let</code> starts a child task immediately; the <code dir=\"ltr\">await</code> collects both results."},{"text":"About 600 ms, because each <code dir=\"ltr\">await</code> waits in turn.","why":"That’s two plain <code dir=\"ltr\">try await</code> calls in sequence. <code dir=\"ltr\">async let</code> starts both before either is awaited."},{"text":"Instantly, because <code dir=\"ltr\">async let</code> doesn’t wait.","why":"The work still takes time; <code dir=\"ltr\">async let</code> only lets it overlap."}],"answer":0},{"q":"<p>Why can you call <code dir=\"ltr\">await cache.save(rate, for: &quot;EUR&quot;)</code> on an actor from many tasks at once without a data race?</p>","options":[{"text":"Actors copy themselves for each caller.","why":"Actors are reference types and are never copied; there’s one instance with one set of state."},{"text":"Actors lock the whole app while they run.","why":"Only that actor’s state is protected, and only one task at a time runs code on it."},{"text":"The actor runs one call at a time on its state, so callers wait their turn with <code dir=\"ltr\">await</code>.","why":"Correct. That serialization is why every outside call to an actor needs <code dir=\"ltr\">await</code>."}],"answer":2},{"q":"<p>Which type is <code dir=\"ltr\">Sendable</code> without you writing anything extra?</p>","options":[{"text":"A <code dir=\"ltr\">final class</code> with a <code dir=\"ltr\">var</code> property.","why":"Mutable shared state is exactly what <code dir=\"ltr\">Sendable</code> rules out; the compiler won’t infer it."},{"text":"A non-public struct whose stored properties are all <code dir=\"ltr\">Sendable</code>, such as <code dir=\"ltr\">String</code> and <code dir=\"ltr\">Decimal</code>.","why":"Correct. Value types made of Sendable values can be copied safely across tasks, so Swift infers it."},{"text":"Any class marked <code dir=\"ltr\">@Observable</code>.","why":"Observable classes are mutable and not Sendable unless they’re isolated to an actor such as <code dir=\"ltr\">@MainActor</code>."}],"answer":1}],"exercise":{"kind":"spot-bug","language":"swift","code":"import Foundation\n\nfinal class Tally {\n    var total: Decimal = 0\n}\n\nfunc totalOfAll(_ amounts: [Decimal]) async -> Decimal {\n    let tally = Tally()\n    await withTaskGroup(of: Void.self) { group in\n        for amount in amounts {\n            group.addTask { tally.total += amount }\n        }\n    }\n    return tally.total\n}","bugLines":[11],"title":"The data race Swift 6 refuses to build","prompt":"<p>This helper totals a list of amounts using a task group. It would run fine in a quick test, yet the Swift 6 compiler refuses to build it. Select the line that creates the data race.</p>\n","hints":["How many child tasks are running at the same time, and what are they all touching?","<code dir=\"ltr\">Tally</code> is an ordinary class with a <code dir=\"ltr\">var</code>. Is it <code dir=\"ltr\">Sendable</code>?","In structured concurrency, children should hand results back to the parent rather than writing to shared state."],"explanation":"<p>Line 11 starts one child task per amount, and every child mutates the same <code dir=\"ltr\">Tally</code> object. Child tasks run in parallel, so two of them can read and write <code dir=\"ltr\">total</code> at the same moment and lose an update. Because <code dir=\"ltr\">Tally</code> isn’t <code dir=\"ltr\">Sendable</code>, Swift 6 rejects the capture (“passing closure as a ‘sending’ parameter risks causing data races”, or a main-actor isolation error when your project uses main-actor default isolation).</p>\n<p>Let each child return its value and sum in the parent, which only ever runs one loop iteration at a time:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> totalOfAll</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">_</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amounts: [Decimal]) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">async</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Decimal {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    await</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> withTaskGroup</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">of</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: Decimal.</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">self</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) { group </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amount </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amounts {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            group.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">addTask</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { amount }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> total: Decimal </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        for</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> part </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> group {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">            total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> part</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> total</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>If the state really must be shared, make <code dir=\"ltr\">Tally</code> an <code dir=\"ltr\">actor</code> and call <code dir=\"ltr\">await tally.add(amount)</code>.</p>\n","checkLabels":{}},"words":782,"readMinutes":4}